Merge "Add shared FingerprintSensorProperty." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 08a09e1..b1f587e 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -334,7 +334,7 @@
aconfig_declarations {
name: "android.app.flags-aconfig",
package: "android.app",
- srcs: ["core/java/android/app/activity_manager.aconfig"],
+ srcs: ["core/java/android/app/*.aconfig"],
}
java_aconfig_library {
diff --git a/Android.bp b/Android.bp
index f1a3af2..4c44974 100644
--- a/Android.bp
+++ b/Android.bp
@@ -525,6 +525,7 @@
required: [
"framework-minus-apex",
"framework-platform-compat-config",
+ "framework-location-compat-config",
"services-platform-compat-config",
"icu4j-platform-compat-config",
"TeleService-platform-compat-config",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 122e627..b215ee4 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -68,7 +68,6 @@
"name": "FrameworksInputMethodSystemServerTests",
"options": [
{"include-filter": "com.android.server.inputmethod"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
index b8fef63..6924cb2 100644
--- a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
@@ -7,7 +7,6 @@
],
"options": [
{"include-filter": "com.android.server.DeviceIdleControllerTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -29,4 +28,4 @@
]
}
]
-}
\ No newline at end of file
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
index b76c582..c0686116 100644
--- a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.DeviceIdleControllerTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
@@ -17,4 +16,4 @@
]
}
]
-}
\ No newline at end of file
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
index 8504b1f..e649485 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
@@ -3,8 +3,6 @@
{
"name": "CtsJobSchedulerTestCases",
"options": [
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.LargeTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.LargeTest"}
]
@@ -13,18 +11,16 @@
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.job"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"}
]
},
{
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.job"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"}
]
}
],
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
index 73b00b6..e194b8d 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.tare"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -12,7 +11,6 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.tare"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
index 9ec799f..a75415e 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "CtsUsageStatsTestCases",
"options": [
{"include-filter": "android.app.usage.cts.UsageStatsTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.MediumTest"},
{"exclude-annotation": "androidx.test.filters.LargeTest"}
@@ -21,7 +20,6 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.usage"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
@@ -37,4 +35,4 @@
]
}
]
-}
\ No newline at end of file
+}
diff --git a/api/Android.bp b/api/Android.bp
index 6986ac0..f017a47 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -91,6 +91,7 @@
"framework-media",
"framework-mediaprovider",
"framework-ondevicepersonalization",
+ "framework-pdf",
"framework-permission",
"framework-permission-s",
"framework-scheduling",
diff --git a/boot/Android.bp b/boot/Android.bp
index 93d425e..b33fab6 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -208,6 +208,13 @@
],
}
+genrule { // This module exists to make the srcjar output available to Make.
+ name: "platform-bootclasspath.srcjar",
+ srcs: [":platform-bootclasspath{.srcjar}"],
+ out: ["platform-bootclasspath.srcjar"],
+ cmd: "cp $(in) $(out)",
+}
+
platform_systemserverclasspath {
name: "platform-systemserverclasspath",
}
diff --git a/cmds/uinput/README.md b/cmds/uinput/README.md
index 82df555..e7361fe 100644
--- a/cmds/uinput/README.md
+++ b/cmds/uinput/README.md
@@ -48,6 +48,7 @@
| `vid` | 16-bit integer | Vendor ID |
| `pid` | 16-bit integer | Product ID |
| `bus` | string | Bus that device should use |
+| `port` | string | `phys` value to report |
| `configuration` | object array | uinput device configuration|
| `ff_effects_max` | integer | `ff_effects_max` value |
| `abs_info` | array | Absolute axes information |
diff --git a/core/api/current.txt b/core/api/current.txt
index 955858b..7fd25b2 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5952,6 +5952,7 @@
public class GrammaticalInflectionManager {
method public int getApplicationGrammaticalGender();
+ method @FlaggedApi("android.app.system_terms_of_address_enabled") public int getSystemGrammaticalGender();
method public void setRequestedApplicationGrammaticalGender(int);
}
@@ -6749,7 +6750,6 @@
method public android.app.Notification.WearableExtender clone();
method public android.app.Notification.Builder extend(android.app.Notification.Builder);
method public java.util.List<android.app.Notification.Action> getActions();
- method @Deprecated public android.graphics.Bitmap getBackground();
method public String getBridgeTag();
method public int getContentAction();
method @Deprecated public int getContentIcon();
@@ -6768,7 +6768,6 @@
method @Deprecated public boolean getHintShowBackgroundOnly();
method @Deprecated public java.util.List<android.app.Notification> getPages();
method public boolean getStartScrollBottom();
- method @Deprecated public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
method public android.app.Notification.WearableExtender setBridgeTag(String);
method public android.app.Notification.WearableExtender setContentAction(int);
method @Deprecated public android.app.Notification.WearableExtender setContentIcon(int);
@@ -42905,7 +42904,7 @@
field public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY = "carrier_service_number_array";
field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
- field public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE = "carrier_supported_satellite_services_per_provider_bundle";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE = "carrier_supported_satellite_services_per_provider_bundle";
field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool";
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_SUPPORTS_TETHERING_BOOL = "carrier_supports_tethering_bool";
@@ -43073,6 +43072,7 @@
field public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL = "rtt_supported_while_roaming_bool";
field public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
field public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL = "rtt_upgrade_supported_for_downgraded_vt_call";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL = "satellite_attach_supported_bool";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
@@ -54456,8 +54456,8 @@
public class AnimationUtils {
ctor public AnimationUtils();
method public static long currentAnimationTimeMillis();
- method public static long getExpectedPresentationTimeMillis();
- method public static long getExpectedPresentationTimeNanos();
+ method @FlaggedApi("android.view.flags.expected_presentation_time_api") public static long getExpectedPresentationTimeMillis();
+ method @FlaggedApi("android.view.flags.expected_presentation_time_api") public static long getExpectedPresentationTimeNanos();
method public static android.view.animation.Animation loadAnimation(android.content.Context, @AnimRes int) throws android.content.res.Resources.NotFoundException;
method public static android.view.animation.Interpolator loadInterpolator(android.content.Context, @AnimRes @InterpolatorRes int) throws android.content.res.Resources.NotFoundException;
method public static android.view.animation.LayoutAnimationController loadLayoutAnimation(android.content.Context, @AnimRes int) throws android.content.res.Resources.NotFoundException;
@@ -55029,10 +55029,12 @@
method public int getInitialToolType();
method @NonNull public java.util.Set<java.lang.Class<? extends android.view.inputmethod.PreviewableHandwritingGesture>> getSupportedHandwritingGesturePreviews();
method @NonNull public java.util.List<java.lang.Class<? extends android.view.inputmethod.HandwritingGesture>> getSupportedHandwritingGestures();
+ method @FlaggedApi("android.view.inputmethod.editorinfo_handwriting_enabled") public boolean isStylusHandwritingEnabled();
method public final void makeCompatible(int);
method public void setInitialSurroundingSubText(@NonNull CharSequence, int);
method public void setInitialSurroundingText(@NonNull CharSequence);
method public void setInitialToolType(int);
+ method @FlaggedApi("android.view.inputmethod.editorinfo_handwriting_enabled") public void setStylusHandwritingEnabled(boolean);
method public void setSupportedHandwritingGesturePreviews(@NonNull java.util.Set<java.lang.Class<? extends android.view.inputmethod.PreviewableHandwritingGesture>>);
method public void setSupportedHandwritingGestures(@NonNull java.util.List<java.lang.Class<? extends android.view.inputmethod.HandwritingGesture>>);
method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 8b3696a..57e2e73 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -23,6 +23,11 @@
method @Deprecated public android.app.Notification.Builder setTimeout(long);
}
+ public static final class Notification.WearableExtender implements android.app.Notification.Extender {
+ method @Deprecated public android.graphics.Bitmap getBackground();
+ method @Deprecated public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
+ }
+
}
package android.app.slice {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1d88e00..8748e69 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3223,14 +3223,14 @@
public final class VirtualDeviceParams implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public java.util.Set<android.content.ComponentName> getAllowedActivities();
- method @NonNull public java.util.Set<android.content.ComponentName> getAllowedCrossTaskNavigations();
+ method @Deprecated @NonNull public java.util.Set<android.content.ComponentName> getAllowedActivities();
+ method @Deprecated @NonNull public java.util.Set<android.content.ComponentName> getAllowedCrossTaskNavigations();
method public int getAudioPlaybackSessionId();
method public int getAudioRecordingSessionId();
- method @NonNull public java.util.Set<android.content.ComponentName> getBlockedActivities();
- method @NonNull public java.util.Set<android.content.ComponentName> getBlockedCrossTaskNavigations();
- method public int getDefaultActivityPolicy();
- method public int getDefaultNavigationPolicy();
+ method @Deprecated @NonNull public java.util.Set<android.content.ComponentName> getBlockedActivities();
+ method @Deprecated @NonNull public java.util.Set<android.content.ComponentName> getBlockedCrossTaskNavigations();
+ method @Deprecated public int getDefaultActivityPolicy();
+ method @Deprecated public int getDefaultNavigationPolicy();
method public int getDevicePolicy(int);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @Nullable public android.content.ComponentName getHomeComponent();
method public int getLockState();
@@ -3238,15 +3238,15 @@
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
method @NonNull public java.util.List<android.companion.virtual.sensor.VirtualSensorConfig> getVirtualSensorConfigs();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0; // 0x0
- field public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1; // 0x1
+ field @Deprecated public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0; // 0x0
+ field @Deprecated public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDeviceParams> CREATOR;
field public static final int DEVICE_POLICY_CUSTOM = 1; // 0x1
field public static final int DEVICE_POLICY_DEFAULT = 0; // 0x0
field public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1; // 0x1
field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
- field public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; // 0x0
- field public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
+ field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; // 0x0
+ field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
field @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
field public static final int POLICY_TYPE_AUDIO = 1; // 0x1
field public static final int POLICY_TYPE_RECENTS = 2; // 0x2
@@ -3257,12 +3257,12 @@
ctor public VirtualDeviceParams.Builder();
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder addVirtualSensorConfig(@NonNull android.companion.virtual.sensor.VirtualSensorConfig);
method @NonNull public android.companion.virtual.VirtualDeviceParams build();
- method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@NonNull java.util.Set<android.content.ComponentName>);
- method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
+ method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@NonNull java.util.Set<android.content.ComponentName>);
+ method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAudioPlaybackSessionId(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAudioRecordingSessionId(int);
- method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
- method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
+ method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
+ method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
@@ -16731,17 +16731,22 @@
}
public final class SatelliteManager {
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void addSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatelliteService(@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> getSatelliteAttachRestrictionReasonsForCarrier(int);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsDemoModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteAttachEnabledForCarrier(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteCommunicationAllowedForCurrentLocation(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method public void requestIsSatelliteSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @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>);
@@ -16768,6 +16773,7 @@
field public static final int NT_RADIO_TECHNOLOGY_NR_NTN = 2; // 0x2
field public static final int NT_RADIO_TECHNOLOGY_PROPRIETARY = 4; // 0x4
field public static final int NT_RADIO_TECHNOLOGY_UNKNOWN = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1; // 0x1
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE = 0; // 0x0
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7; // 0x7
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE = 6; // 0x6
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 25c48e6..9a90df9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -62,7 +62,7 @@
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ActivityResultItem;
import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.DestroyActivityItem;
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.PendingTransactionActions.StopInfo;
@@ -263,6 +263,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
@@ -375,8 +376,8 @@
@GuardedBy("mPendingOverrideConfigs")
private final ArrayMap<IBinder, Configuration> mPendingOverrideConfigs = new ArrayMap<>();
/** The activities to be truly destroyed (not include relaunch). */
- final Map<IBinder, ClientTransactionItem> mActivitiesToBeDestroyed =
- Collections.synchronizedMap(new ArrayMap<IBinder, ClientTransactionItem>());
+ final Map<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed =
+ Collections.synchronizedMap(new ArrayMap<>());
// List of new activities that should be reported when next we idle.
final ArrayList<ActivityClientRecord> mNewActivities = new ArrayList<>();
// Number of activities that are currently visible on-screen.
@@ -3033,7 +3034,7 @@
"%13s %8s %8s %8s %8s %8s %8s %8s %8s";
private static final String ONE_COUNT_COLUMN = "%21s %8d";
private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d";
- private static final String THREE_COUNT_COLUMNS = "%21s %8d %21s %8s %21s %8d";
+ private static final String THREE_COUNT_COLUMNS = "%21s %8d %21s %8d %21s %8d";
private static final String TWO_COUNT_COLUMN_HEADER = "%21s %8s %21s %8s";
private static final String ONE_ALT_COUNT_COLUMN = "%21s %8s %21s %8d";
@@ -3041,7 +3042,7 @@
private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 4;
static void printRow(PrintWriter pw, String format, Object...objs) {
- pw.println(String.format(format, objs));
+ pw.println(String.format(Locale.US, format, objs));
}
@NeverCompile
@@ -5799,7 +5800,7 @@
}
@Override
- public Map<IBinder, ClientTransactionItem> getActivitiesToBeDestroyed() {
+ public Map<IBinder, DestroyActivityItem> getActivitiesToBeDestroyed() {
return mActivitiesToBeDestroyed;
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4f9225f..ca10d14 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2336,6 +2336,7 @@
OP_WRITE_MEDIA_VIDEO,
OP_READ_MEDIA_IMAGES,
OP_WRITE_MEDIA_IMAGES,
+ OP_READ_MEDIA_VISUAL_USER_SELECTED,
// Nearby devices
OP_BLUETOOTH_SCAN,
OP_BLUETOOTH_CONNECT,
@@ -2376,7 +2377,6 @@
OP_MANAGE_MEDIA,
OP_TURN_SCREEN_ON,
OP_RUN_USER_INITIATED_JOBS,
- OP_READ_MEDIA_VISUAL_USER_SELECTED,
OP_FOREGROUND_SERVICE_SPECIAL_USE,
OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
OP_USE_FULL_SCREEN_INTENT
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 98020ff..25075e9 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -19,7 +19,7 @@
import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.DestroyActivityItem;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.TransactionExecutor;
import android.content.Context;
@@ -108,7 +108,7 @@
// and deliver callbacks.
/** Get activity and its corresponding transaction item which are going to destroy. */
- public abstract Map<IBinder, ClientTransactionItem> getActivitiesToBeDestroyed();
+ public abstract Map<IBinder, DestroyActivityItem> getActivitiesToBeDestroyed();
/** Destroy the activity. */
public abstract void handleDestroyActivity(@NonNull ActivityClientRecord r, boolean finishing,
diff --git a/core/java/android/app/GrammaticalInflectionManager.java b/core/java/android/app/GrammaticalInflectionManager.java
index 1905b6a..bc6fe61 100644
--- a/core/java/android/app/GrammaticalInflectionManager.java
+++ b/core/java/android/app/GrammaticalInflectionManager.java
@@ -16,12 +16,15 @@
package android.app;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -31,11 +34,15 @@
*/
@SystemService(Context.GRAMMATICAL_INFLECTION_SERVICE)
public class GrammaticalInflectionManager {
- private static final Set<Integer> VALID_GENDER_VALUES = new HashSet<>(Arrays.asList(
+
+ /** @hide */
+ @NonNull
+ public static final Set<Integer> VALID_GRAMMATICAL_GENDER_VALUES =
+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED,
Configuration.GRAMMATICAL_GENDER_NEUTRAL,
Configuration.GRAMMATICAL_GENDER_FEMININE,
- Configuration.GRAMMATICAL_GENDER_MASCULINE));
+ Configuration.GRAMMATICAL_GENDER_MASCULINE)));
private final Context mContext;
private final IGrammaticalInflectionManager mService;
@@ -79,7 +86,7 @@
*/
public void setRequestedApplicationGrammaticalGender(
@Configuration.GrammaticalGender int grammaticalGender) {
- if (!VALID_GENDER_VALUES.contains(grammaticalGender)) {
+ if (!VALID_GRAMMATICAL_GENDER_VALUES.contains(grammaticalGender)) {
throw new IllegalArgumentException("Unknown grammatical gender");
}
@@ -90,4 +97,44 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Sets the current grammatical gender for all privileged applications. The value will be
+ * stored in an encrypted file at {@link android.os.Environment#getDataSystemCeDirectory(int)
+ *
+ * @param grammaticalGender the terms of address the user preferred in system.
+ *
+ * @see Configuration#getGrammaticalGender
+ * @hide
+ */
+ public void setSystemWideGrammaticalGender(
+ @Configuration.GrammaticalGender int grammaticalGender) {
+ if (!VALID_GRAMMATICAL_GENDER_VALUES.contains(grammaticalGender)) {
+ throw new IllegalArgumentException("Unknown grammatical gender");
+ }
+
+ try {
+ mService.setSystemWideGrammaticalGender(mContext.getUserId(), grammaticalGender);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the current grammatical gender of privileged application from the encrypted file,
+ * which is stored under {@link android.os.Environment#getDataSystemCeDirectory(int)}.
+ *
+ * @return the value of grammatical gender
+ *
+ * @see Configuration#getGrammaticalGender
+ */
+ @FlaggedApi(Flags.FLAG_SYSTEM_TERMS_OF_ADDRESS_ENABLED)
+ @Configuration.GrammaticalGender
+ public int getSystemGrammaticalGender() {
+ try {
+ return mService.getSystemGrammaticalGender(mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/IGrammaticalInflectionManager.aidl b/core/java/android/app/IGrammaticalInflectionManager.aidl
index 9366a45..48a4841 100644
--- a/core/java/android/app/IGrammaticalInflectionManager.aidl
+++ b/core/java/android/app/IGrammaticalInflectionManager.aidl
@@ -16,4 +16,14 @@
* Sets a specified app’s app-specific grammatical gender.
*/
void setRequestedApplicationGrammaticalGender(String appPackageName, int userId, int gender);
- }
\ No newline at end of file
+
+ /**
+ * Sets the grammatical gender to system.
+ */
+ void setSystemWideGrammaticalGender(int userId, int gender);
+
+ /**
+ * Gets the grammatical gender from system.
+ */
+ int getSystemGrammaticalGender(int userId);
+ }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2c42df3..93c2b5a 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -44,6 +44,9 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.admin.DevicePolicyManager;
+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.Intent;
@@ -296,6 +299,15 @@
public static final String EXTRA_REMOTE_INPUT_DRAFT = "android.remoteInputDraft";
/**
+ * The call to WearableExtender#setBackground(Bitmap) will have no effect and the passed
+ * Bitmap will not be retained in memory.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @VisibleForTesting
+ static final long WEARABLE_EXTENDER_BACKGROUND_BLOCKED = 270551184L;
+
+ /**
* A timestamp related to this notification, in milliseconds since the epoch.
*
* Default value: {@link System#currentTimeMillis() Now}.
@@ -11148,9 +11160,20 @@
wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
new Notification[mPages.size()]));
}
+
if (mBackground != null) {
- wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
+ // Keeping WearableExtender backgrounds in memory despite them being deprecated has
+ // added noticeable increase in system server and system ui memory usage. After
+ // target VERSION_CODE#VANILLA_ICE_CREAM the background will not be populated
+ // anymore.
+ if (CompatChanges.isChangeEnabled(WEARABLE_EXTENDER_BACKGROUND_BLOCKED)) {
+ Log.d(TAG, "Use of background in WearableExtenders has been deprecated and "
+ + "will not be populated anymore.");
+ } else {
+ wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
+ }
}
+
if (mContentIcon != 0) {
wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
}
@@ -11369,12 +11392,21 @@
*
* @param background the background bitmap
* @return this object for method chaining
- * @see android.app.Notification.WearableExtender#getBackground
- * @deprecated Background images are no longer supported.
+ * @removed Not functional since {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}.
+ * The wearable background is not used by wearables anymore and uses up
+ * unnecessary memory.
*/
@Deprecated
public WearableExtender setBackground(Bitmap background) {
- mBackground = background;
+ // Keeping WearableExtender backgrounds in memory despite them being deprecated has
+ // added noticeable increase in system server and system ui memory usage. After
+ // target VERSION_CODE#VANILLA_ICE_CREAM the background will not be populated anymore.
+ if (CompatChanges.isChangeEnabled(WEARABLE_EXTENDER_BACKGROUND_BLOCKED)) {
+ Log.d(TAG, "Use of background in WearableExtenders has been deprecated and "
+ + "will not be populated anymore.");
+ } else {
+ mBackground = background;
+ }
return this;
}
@@ -11384,11 +11416,13 @@
* will work with any notification style.
*
* @return the background image
- * @see android.app.Notification.WearableExtender#setBackground
- * @deprecated Background images are no longer supported.
+ * @removed Not functional since {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}. The
+ * wearable background is not used by wearables anymore and uses up
+ * unnecessary memory.
*/
@Deprecated
public Bitmap getBackground() {
+ Log.w(TAG, "Use of background in WearableExtender has been removed, returning null.");
return mBackground;
}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index e1c45d9..9cf54e3 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -54,6 +54,9 @@
per-file Broadcast* = file:/BROADCASTS_OWNERS
per-file ReceiverInfo* = file:/BROADCASTS_OWNERS
+# GrammaticalInflectionManager
+per-file *GrammaticalInflection* = file:/services/core/java/com/android/server/grammaticalinflection/OWNERS
+
# KeyguardManager
per-file KeyguardManager.java = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 315a055..a29c196 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -19,7 +19,7 @@
"name": "CtsAppOpsTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
diff --git a/core/java/android/app/grammatical_inflection_manager.aconfig b/core/java/android/app/grammatical_inflection_manager.aconfig
new file mode 100644
index 0000000..989ce61
--- /dev/null
+++ b/core/java/android/app/grammatical_inflection_manager.aconfig
@@ -0,0 +1,8 @@
+package: "android.app"
+
+flag {
+ name: "system_terms_of_address_enabled"
+ namespace: "grammatical_gender"
+ description: "Feature flag for System Terms of Address"
+ bug: "297798866"
+}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index a5b0f18..8617386 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -23,7 +23,6 @@
import android.app.ClientTransactionHandler;
import android.app.IApplicationThread;
import android.compat.annotation.UnsupportedAppUsage;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -83,23 +82,6 @@
return mActivityCallbacks;
}
- /** Get the target activity. */
- @Nullable
- @UnsupportedAppUsage
- public IBinder getActivityToken() {
- // TODO(b/260873529): remove after we allow multiple activity items in one transaction.
- if (mLifecycleStateRequest != null) {
- return mLifecycleStateRequest.getActivityToken();
- }
- for (int i = mActivityCallbacks.size() - 1; i >= 0; i--) {
- final IBinder token = mActivityCallbacks.get(i).getActivityToken();
- if (token != null) {
- return token;
- }
- }
- return null;
- }
-
/** Get the target state lifecycle request. */
@VisibleForTesting(visibility = PACKAGE)
@UnsupportedAppUsage
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index ddb6df1..f9cf075 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -50,6 +50,13 @@
}
@Override
+ public void postExecute(@NonNull ClientTransactionHandler client,
+ @NonNull PendingTransactionActions pendingActions) {
+ // Cleanup after execution.
+ client.getActivitiesToBeDestroyed().remove(getActivityToken());
+ }
+
+ @Override
public int getTargetState() {
return ON_DESTROY;
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 4433673..066f9fe 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -47,7 +47,6 @@
import com.android.internal.annotations.VisibleForTesting;
import java.util.List;
-import java.util.Map;
/**
* Class that manages transaction execution in the correct order.
@@ -75,34 +74,14 @@
* either remain in the initial state, or last state needed by a callback.
*/
public void execute(@NonNull ClientTransaction transaction) {
- if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");
-
- final IBinder token = transaction.getActivityToken();
- if (token != null) {
- final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed =
- mTransactionHandler.getActivitiesToBeDestroyed();
- final ClientTransactionItem destroyItem = activitiesToBeDestroyed.get(token);
- if (destroyItem != null) {
- if (transaction.getLifecycleStateRequest() == destroyItem) {
- // It is going to execute the transaction that will destroy activity with the
- // token, so the corresponding to-be-destroyed record can be removed.
- activitiesToBeDestroyed.remove(token);
- }
- if (mTransactionHandler.getActivityClient(token) == null) {
- // The activity has not been created but has been requested to destroy, so all
- // transactions for the token are just like being cancelled.
- Slog.w(TAG, tId(transaction) + "Skip pre-destroyed transaction:\n"
- + transactionToString(transaction, mTransactionHandler));
- return;
- }
- }
+ if (DEBUG_RESOLVER) {
+ Slog.d(TAG, tId(transaction) + "Start resolving transaction");
+ Slog.d(TAG, transactionToString(transaction, mTransactionHandler));
}
- if (DEBUG_RESOLVER) Slog.d(TAG, transactionToString(transaction, mTransactionHandler));
-
executeCallbacks(transaction);
-
executeLifecycleState(transaction);
+
mPendingActions.clear();
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
}
@@ -135,6 +114,14 @@
final IBinder token = item.getActivityToken();
ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+ if (token != null && r == null
+ && mTransactionHandler.getActivitiesToBeDestroyed().containsKey(token)) {
+ // The activity has not been created but has been requested to destroy, so all
+ // transactions for the token are just like being cancelled.
+ Slog.w(TAG, "Skip pre-destroyed transaction item:\n" + item);
+ continue;
+ }
+
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
final int postExecutionState = item.getPostExecutionState();
@@ -211,6 +198,10 @@
}
if (r == null) {
+ if (mTransactionHandler.getActivitiesToBeDestroyed().get(token) == lifecycleItem) {
+ // Always cleanup for destroy item.
+ lifecycleItem.postExecute(mTransactionHandler, mPendingActions);
+ }
// Ignore requests for non-existent client records for now.
return;
}
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index bf00a5a..2f97080 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -23,6 +23,8 @@
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
+import android.companion.virtual.camera.IVirtualCamera;
+import android.companion.virtual.camera.VirtualCameraHalConfig;
import android.content.ComponentName;
import android.content.IntentFilter;
import android.graphics.Point;
@@ -232,4 +234,10 @@
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
void unregisterIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor);
+
+ /**
+ * Creates a new VirtualCamera and registers it with the VirtualCameraProvider.
+ */
+ @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+ void registerVirtualCamera(in IVirtualCamera camera);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index 7bf2e91..f6a7d2a 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -22,6 +22,8 @@
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.companion.virtual.audio.VirtualAudioDevice;
+import android.companion.virtual.camera.VirtualCamera;
+import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtual.sensor.VirtualSensor;
import android.content.ComponentName;
import android.content.Context;
@@ -339,6 +341,11 @@
}
@NonNull
+ VirtualCamera createVirtualCamera(@NonNull VirtualCameraConfig config) {
+ return new VirtualCamera(mVirtualDevice, config);
+ }
+
+ @NonNull
void setShowPointerIcon(boolean showPointerIcon) {
try {
mVirtualDevice.setShowPointerIcon(showPointerIcon);
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index d338d17..baed7f9 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -33,6 +33,8 @@
import android.companion.AssociationInfo;
import android.companion.virtual.audio.VirtualAudioDevice;
import android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback;
+import android.companion.virtual.camera.VirtualCamera;
+import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtual.flags.Flags;
import android.companion.virtual.sensor.VirtualSensor;
import android.content.ComponentName;
@@ -851,6 +853,24 @@
}
/**
+ * Creates a new virtual camera. If a virtual camera was already created, it will be closed.
+ *
+ * @param config camera config.
+ * @return newly created camera;
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @NonNull
+ @FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
+ public VirtualCamera createVirtualCamera(@NonNull VirtualCameraConfig config) {
+ if (!Flags.virtualCamera()) {
+ throw new UnsupportedOperationException(
+ "Flag is not enabled: %s".formatted(Flags.FLAG_VIRTUAL_CAMERA));
+ }
+ return mVirtualDeviceInternal.createVirtualCamera(Objects.requireNonNull(config));
+ }
+
+ /**
* Sets the visibility of the pointer icon for this VirtualDevice's associated displays.
*
* @param showPointerIcon True if the pointer should be shown; false otherwise. The default
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 0fa78c8..0975cbb 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -94,13 +94,19 @@
/**
* Indicates that activities are allowed by default on this virtual device, unless they are
* explicitly blocked by {@link Builder#setBlockedActivities}.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and {@link #DEVICE_POLICY_DEFAULT}
*/
+ @Deprecated
public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0;
/**
* Indicates that activities are blocked by default on this virtual device, unless they are
* allowed by {@link Builder#setAllowedActivities}.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and {@link #DEVICE_POLICY_CUSTOM}
*/
+ @Deprecated
public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1;
/** @hide */
@@ -113,13 +119,19 @@
/**
* Indicates that tasks are allowed to navigate to other tasks on this virtual device,
* unless they are explicitly blocked by {@link Builder#setBlockedCrossTaskNavigations}.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and {@link #DEVICE_POLICY_DEFAULT}
*/
+ @Deprecated
public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0;
/**
* Indicates that tasks are blocked from navigating to other tasks by default on this virtual
* device, unless allowed by {@link Builder#setAllowedCrossTaskNavigations}.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and {@link #DEVICE_POLICY_CUSTOM}
*/
+ @Deprecated
public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1;
/** @hide */
@@ -325,7 +337,10 @@
* be be allowed by default.
*
* @see Builder#setAllowedCrossTaskNavigations(Set)
+ *
+ * @deprecated See {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Set<ComponentName> getAllowedCrossTaskNavigations() {
return mDefaultNavigationPolicy == NAVIGATION_POLICY_DEFAULT_ALLOWED
@@ -340,7 +355,10 @@
* will be be allowed by default.
*
* @see Builder#setBlockedCrossTaskNavigations(Set)
+ *
+ * @deprecated See {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Set<ComponentName> getBlockedCrossTaskNavigations() {
return mDefaultNavigationPolicy == NAVIGATION_POLICY_DEFAULT_BLOCKED
@@ -355,7 +373,10 @@
*
* @see Builder#setAllowedCrossTaskNavigations
* @see Builder#setBlockedCrossTaskNavigations
+ *
+ * @deprecated Use {@link #getDevicePolicy} with {@link #POLICY_TYPE_ACTIVITY}
*/
+ @Deprecated
@NavigationPolicy
public int getDefaultNavigationPolicy() {
return mDefaultNavigationPolicy;
@@ -366,7 +387,10 @@
* allowed, except the ones explicitly blocked.
*
* @see Builder#setAllowedActivities(Set)
+ *
+ * @deprecated See {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Set<ComponentName> getAllowedActivities() {
return mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_ALLOWED
@@ -379,7 +403,10 @@
* that all activities in {@link #getAllowedActivities} are allowed.
*
* @see Builder#setBlockedActivities(Set)
+ *
+ * @deprecated See {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Set<ComponentName> getBlockedActivities() {
return mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED
@@ -394,7 +421,10 @@
*
* @see Builder#setBlockedActivities
* @see Builder#setAllowedActivities
+ *
+ * @deprecated Use {@link #getDevicePolicy} with {@link #POLICY_TYPE_ACTIVITY}
*/
+ @Deprecated
@ActivityPolicy
public int getDefaultActivityPolicy() {
return mDefaultActivityPolicy;
@@ -743,7 +773,11 @@
*
* @param allowedCrossTaskNavigations A set of tasks {@link ComponentName} allowed to
* navigate to new tasks in the virtual device.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and
+ * {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Builder setAllowedCrossTaskNavigations(
@NonNull Set<ComponentName> allowedCrossTaskNavigations) {
@@ -774,7 +808,11 @@
*
* @param blockedCrossTaskNavigations A set of tasks {@link ComponentName} to be
* blocked from navigating to new tasks in the virtual device.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and
+ * {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Builder setBlockedCrossTaskNavigations(
@NonNull Set<ComponentName> blockedCrossTaskNavigations) {
@@ -802,7 +840,11 @@
*
* @param allowedActivities A set of activity {@link ComponentName} allowed to be launched
* in the virtual device.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and
+ * {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Builder setAllowedActivities(@NonNull Set<ComponentName> allowedActivities) {
if (mDefaultActivityPolicyConfigured
@@ -828,7 +870,11 @@
*
* @param blockedActivities A set of {@link ComponentName} to be blocked launching from
* virtual device.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and
+ * {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Builder setBlockedActivities(@NonNull Set<ComponentName> blockedActivities) {
if (mDefaultActivityPolicyConfigured
diff --git a/core/java/android/companion/virtual/camera/IVirtualCamera.aidl b/core/java/android/companion/virtual/camera/IVirtualCamera.aidl
new file mode 100644
index 0000000..58b850d
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/IVirtualCamera.aidl
@@ -0,0 +1,17 @@
+package android.companion.virtual.camera;
+
+import android.companion.virtual.camera.IVirtualCameraSession;
+import android.companion.virtual.camera.VirtualCameraHalConfig;
+
+/**
+ * Counterpart of ICameraDevice for virtual camera.
+ *
+ * @hide
+ */
+interface IVirtualCamera {
+
+ IVirtualCameraSession open();
+
+ VirtualCameraHalConfig getHalConfig();
+
+}
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl b/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
new file mode 100644
index 0000000..2529807
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/IVirtualCameraSession.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.companion.virtual.camera;
+
+/**
+ * Counterpart of ICameraDeviceSession for virtual camera.
+ *
+ * @hide
+ */
+interface IVirtualCameraSession {
+
+ void configureStream(int width, int height, int format);
+
+ void close();
+}
diff --git a/core/java/android/companion/virtual/camera/VirtualCamera.java b/core/java/android/companion/virtual/camera/VirtualCamera.java
new file mode 100644
index 0000000..791bf0a
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/VirtualCamera.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual.camera;
+
+import android.companion.virtual.IVirtualDevice;
+import android.os.RemoteException;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * Virtual camera that is used to send image data into system.
+ *
+ * @hide
+ */
+public final class VirtualCamera extends IVirtualCamera.Stub {
+
+ private final VirtualCameraConfig mConfig;
+
+ /**
+ * VirtualCamera device constructor.
+ *
+ * @param virtualDevice The Binder object representing this camera in the server.
+ * @param config Configuration for the new virtual camera
+ */
+ public VirtualCamera(
+ @NonNull IVirtualDevice virtualDevice, @NonNull VirtualCameraConfig config) {
+ mConfig = Objects.requireNonNull(config);
+ Objects.requireNonNull(virtualDevice);
+ try {
+ virtualDevice.registerVirtualCamera(this);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Get the camera session associated with this device */
+ @Override
+ public IVirtualCameraSession open() {
+ // TODO: b/302255544 - Make this async.
+ VirtualCameraSession session = mConfig.getCallback().onOpenSession();
+ return new VirtualCameraSessionInternal(session);
+ }
+
+ /** Returns the configuration of this virtual camera instance. */
+ @NonNull
+ public VirtualCameraConfig getConfig() {
+ return mConfig;
+ }
+
+ /**
+ * Returns the configuration to be used by the virtual camera HAL.
+ *
+ * @hide
+ */
+ @Override
+ @NonNull
+ public VirtualCameraHalConfig getHalConfig() {
+ return mConfig.getHalConfig();
+ }
+}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraCallback.java b/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
new file mode 100644
index 0000000..a7c3d4f
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual.camera;
+
+import android.hardware.camera2.params.SessionConfiguration;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Interface to be provided when creating a new {@link VirtualCamera} in order to receive callbacks
+ * from the framework and the camera system.
+ *
+ * @see VirtualCameraConfig.Builder#setCallback(Executor, VirtualCameraCallback)
+ * @hide
+ */
+public interface VirtualCameraCallback {
+
+ /**
+ * Called when a client opens a new camera session for the associated {@link VirtualCamera}
+ *
+ * @see android.hardware.camera2.CameraDevice#createCaptureSession(SessionConfiguration)
+ */
+ VirtualCameraSession onOpenSession();
+}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraConfig.java b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
new file mode 100644
index 0000000..fb464d5
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual.camera;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.graphics.ImageFormat;
+import android.util.ArraySet;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+/**
+ * Configuration to create a new {@link VirtualCamera}.
+ *
+ * <p>Instance of this class are created using the {@link VirtualCameraConfig.Builder}.
+ *
+ * @hide
+ */
+public final class VirtualCameraConfig {
+
+ private final String mDisplayName;
+ private final Set<VirtualCameraStreamConfig> mStreamConfigurations;
+ private final VirtualCameraCallback mCallback;
+ private final Executor mCallbackExecutor;
+
+ /**
+ * Builder for {@link VirtualCameraConfig}.
+ *
+ * <p>To build an instance of {@link VirtualCameraConfig} the following conditions must be met:
+ * <li>At least one stream must be added wit {@link #addStreamConfiguration(int, int, int)}.
+ * <li>A name must be set with {@link #setDisplayName(String)}
+ * <li>A callback must be set wit {@link #setCallback(Executor, VirtualCameraCallback)}
+ */
+ public static final class Builder {
+
+ private String mDisplayName;
+ private final ArraySet<VirtualCameraStreamConfig> mStreamConfiguration = new ArraySet<>();
+ private Executor mCallbackExecutor;
+ private VirtualCameraCallback mCallback;
+
+ /** Set the visible name of this camera for the user. */
+ // TODO: b/290172356 - Take a resource id instead of displayName
+ @NonNull
+ public Builder setDisplayName(@NonNull String displayName) {
+ mDisplayName = requireNonNull(displayName);
+ return this;
+ }
+
+ /**
+ * Add an available stream configuration fot this {@link VirtualCamera}.
+ *
+ * <p>At least one {@link VirtualCameraStreamConfig} must be added.
+ *
+ * @param width The width of the stream
+ * @param height The height of the stream
+ * @param format The {@link ImageFormat} of the stream
+ */
+ @NonNull
+ public Builder addStreamConfiguration(
+ int width, int height, @ImageFormat.Format int format) {
+ VirtualCameraStreamConfig streamConfig = new VirtualCameraStreamConfig();
+ streamConfig.width = width;
+ streamConfig.height = height;
+ streamConfig.format = format;
+ mStreamConfiguration.add(streamConfig);
+ return this;
+ }
+
+ /**
+ * Sets the {@link VirtualCameraCallback} used by the framework to communicate with the
+ * {@link VirtualCamera} owner.
+ *
+ * <p>Setting a callback is mandatory.
+ *
+ * @param executor The executor onto which the callback methods will be called
+ * @param callback The instance of the callback to be added. Subsequent call to this method
+ * will replace the callback set.
+ */
+ public Builder setCallback(
+ @NonNull Executor executor, @NonNull VirtualCameraCallback callback) {
+ mCallbackExecutor = requireNonNull(executor);
+ mCallback = requireNonNull(callback);
+ return this;
+ }
+
+ /**
+ * Builds a new instance of {@link VirtualCameraConfig}
+ *
+ * @throws NullPointerException if some required parameters are missing.
+ */
+ @NonNull
+ public VirtualCameraConfig build() {
+ return new VirtualCameraConfig(
+ mDisplayName, mStreamConfiguration, mCallbackExecutor, mCallback);
+ }
+ }
+
+ private VirtualCameraConfig(
+ @NonNull String displayName,
+ @NonNull Set<VirtualCameraStreamConfig> streamConfigurations,
+ @NonNull Executor executor,
+ @NonNull VirtualCameraCallback callback) {
+ mDisplayName = requireNonNull(displayName, "Missing display name");
+ mStreamConfigurations =
+ Collections.unmodifiableSet(
+ requireNonNull(streamConfigurations, "Missing stream configuration"));
+ if (mStreamConfigurations.isEmpty()) {
+ throw new IllegalArgumentException(
+ "At least one StreamConfiguration is needed to create a virtual camera.");
+ }
+ mCallback = requireNonNull(callback, "Missing callback");
+ mCallbackExecutor = requireNonNull(executor, "Missing callback executor");
+ }
+
+ /**
+ * @return The display name of this VirtualCamera
+ */
+ @NonNull
+ public String getDisplayName() {
+ return mDisplayName;
+ }
+
+ /**
+ * Returns an unmodifiable set of the stream configurations added to this {@link
+ * VirtualCameraConfig}.
+ *
+ * @see VirtualCameraConfig.Builder#addStreamConfiguration(int, int, int)
+ */
+ @NonNull
+ public Set<VirtualCameraStreamConfig> getStreamConfigs() {
+ return mStreamConfigurations;
+ }
+
+ /** Returns the callback used to communicate from the server to the client. */
+ @NonNull
+ public VirtualCameraCallback getCallback() {
+ return mCallback;
+ }
+
+ /** Returns the executor onto which the callback should be run. */
+ @NonNull
+ public Executor getCallbackExecutor() {
+ return mCallbackExecutor;
+ }
+
+ /**
+ * Returns a new instance of {@link VirtualCameraHalConfig} initialized with data from this
+ * {@link VirtualCameraConfig}
+ */
+ @NonNull
+ public VirtualCameraHalConfig getHalConfig() {
+ VirtualCameraHalConfig halConfig = new VirtualCameraHalConfig();
+ halConfig.displayName = mDisplayName;
+ halConfig.streamConfigs = mStreamConfigurations.toArray(new VirtualCameraStreamConfig[0]);
+ return halConfig;
+ }
+}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraHalConfig.aidl b/core/java/android/companion/virtual/camera/VirtualCameraHalConfig.aidl
new file mode 100644
index 0000000..7070a38
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/VirtualCameraHalConfig.aidl
@@ -0,0 +1,12 @@
+package android.companion.virtual.camera;
+
+import android.companion.virtual.camera.VirtualCameraStreamConfig;
+
+/**
+ * Configuration for VirtualCamera to be passed to the server and HAL service.
+ * @hide
+ */
+parcelable VirtualCameraHalConfig {
+ String displayName;
+ VirtualCameraStreamConfig[] streamConfigs;
+}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraSession.java b/core/java/android/companion/virtual/camera/VirtualCameraSession.java
new file mode 100644
index 0000000..c25d977
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/VirtualCameraSession.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual.camera;
+
+/***
+ * Counterpart of {@link android.hardware.camera2.CameraCaptureSession} for producing
+ * images from a {@link VirtualCamera}.
+ * @hide
+ */
+// TODO: b/289881985 - This is just a POC implementation for now, this will be extended
+// to a full featured Camera Session
+public interface VirtualCameraSession {
+
+ /** Close the session and release its resources. */
+ default void close() {}
+}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraSessionInternal.java b/core/java/android/companion/virtual/camera/VirtualCameraSessionInternal.java
new file mode 100644
index 0000000..da168de
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/VirtualCameraSessionInternal.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual.camera;
+
+import android.graphics.ImageFormat;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * Wraps the client side {@link VirtualCameraSession} into an {@link IVirtualCameraSession}.
+ *
+ * @hide
+ */
+final class VirtualCameraSessionInternal extends IVirtualCameraSession.Stub {
+
+ @SuppressWarnings("FieldCanBeLocal")
+ // TODO: b/289881985: Will be used once connected with the CameraService
+ private final VirtualCameraSession mVirtualCameraSession;
+
+ VirtualCameraSessionInternal(@NonNull VirtualCameraSession virtualCameraSession) {
+ mVirtualCameraSession = Objects.requireNonNull(virtualCameraSession);
+ }
+
+ @Override
+ public void configureStream(int width, int height, @ImageFormat.Format int format) {}
+
+ public void close() {}
+}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.aidl b/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.aidl
new file mode 100644
index 0000000..304d455
--- /dev/null
+++ b/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.companion.virtual.camera;
+
+/**
+ * A stream configuration supported by a virtual camera
+ * @hide
+ */
+parcelable VirtualCameraStreamConfig {
+ int width;
+ int height;
+ int format;
+}
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index ab669cc..b0ab11f 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -45,7 +45,8 @@
]
},
{
- "name":"CarrierAppIntegrationTestCases"
+ "name":"CarrierAppIntegrationTestCases",
+ "keywords": ["internal"]
},
{
"name":"CtsSilentUpdateHostTestCases"
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 0c8eb02..08d32c3 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -13,3 +13,10 @@
description: "Feature flag to enable the archiving feature."
bug: "278553670"
}
+
+flag {
+ name: "stay_stopped"
+ namespace: "backstage_power"
+ description: "Feature flag to improve stopped state enforcement"
+ bug: "296644915"
+}
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 727716e..12442ba 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -147,8 +147,8 @@
*
* <p>Consists of (from the LSB):
* <li>
- * <ul>132bit: Station ID number.
- * <ul>14bit: HD_SUBCHANNEL.
+ * <ul>32bit: Station ID number.
+ * <ul>4bit: HD_SUBCHANNEL.
* <ul>18bit: AMFM_FREQUENCY.
* </li>
*
diff --git a/core/java/android/net/metrics/WakeupEvent.java b/core/java/android/net/metrics/WakeupEvent.java
index af9a73c..53a3ea5 100644
--- a/core/java/android/net/metrics/WakeupEvent.java
+++ b/core/java/android/net/metrics/WakeupEvent.java
@@ -29,7 +29,7 @@
public String iface;
public int uid;
public int ethertype;
- public MacAddress dstHwAddr;
+ public MacAddress dstHwAddr; // actually used to store a src mac address
public String srcIp;
public String dstIp;
public int ipNextHeader;
@@ -44,7 +44,7 @@
j.add(iface);
j.add("uid: " + Integer.toString(uid));
j.add("eth=0x" + Integer.toHexString(ethertype));
- j.add("dstHw=" + dstHwAddr);
+ j.add("srcMac=" + dstHwAddr); // really!! http://b/292404319#comment11
if (ipNextHeader > 0) {
j.add("ipNxtHdr=" + ipNextHeader);
j.add("srcIp=" + srcIp);
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index ad3abd9..2d6e09a 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -8,7 +8,6 @@
"name": "FrameworksVibratorCoreTests",
"options": [
{"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
@@ -21,7 +20,6 @@
"name": "FrameworksVibratorServicesTests",
"options": [
{"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
@@ -34,7 +32,6 @@
"name": "CtsVibratorTestCases",
"options": [
{"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 80b7d40..4c8ef97 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2940,6 +2940,12 @@
* Used to check if the context user is a restricted profile. Restricted profiles
* may have a reduced number of available apps, app restrictions, and account restrictions.
*
+ * <p>The caller must be in the same profile group as the context user or else hold
+ * <li>{@link android.Manifest.permission#MANAGE_USERS},
+ * <li>or {@link android.Manifest.permission#CREATE_USERS},
+ * <li>or, for devices after {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * {@link android.Manifest.permission#QUERY_USERS}.
+ *
* @return whether the context user is a restricted profile.
* @hide
*/
@@ -2963,6 +2969,12 @@
* Check if a user is a restricted profile. Restricted profiles may have a reduced number of
* available apps, app restrictions, and account restrictions.
*
+ * <p>Requires
+ * <li>{@link android.Manifest.permission#MANAGE_USERS},
+ * <li>or {@link android.Manifest.permission#CREATE_USERS},
+ * <li>or, for devices after {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * {@link android.Manifest.permission#QUERY_USERS}.
+ *
* @param user the user to check
* @return whether the user is a restricted profile.
* @hide
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 77be5d4..1294f98 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -6,3 +6,10 @@
description: "enable device aware permission APIs"
bug: "274852670"
}
+
+flag {
+ name: "voice_activation_permission_apis"
+ namespace: "permissions"
+ description: "enable voice activation permission APIs"
+ bug: "287264308"
+}
\ No newline at end of file
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e829ca2..f40232b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9908,6 +9908,48 @@
*/
public static final String DOCK_SETUP_STATE = "dock_setup_state";
+
+ /**
+ * Default, indicates that the user has not yet started the hub mode tutorial.
+ *
+ * @hide
+ */
+ public static final int HUB_MODE_TUTORIAL_NOT_STARTED = 0;
+
+ /**
+ * Indicates that the user has started but not yet completed the hub mode tutorial.
+ * One of the possible states for {@link #HUB_MODE_TUTORIAL_STATE}.
+ *
+ * @hide
+ */
+ public static final int HUB_MODE_TUTORIAL_STARTED = 1;
+
+ /**
+ * Indicates that the user has completed the hub mode tutorial.
+ * One of the possible states for {@link #HUB_MODE_TUTORIAL_STATE}.
+ *
+ * @hide
+ */
+ public static final int HUB_MODE_TUTORIAL_COMPLETED = 10;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ HUB_MODE_TUTORIAL_NOT_STARTED,
+ HUB_MODE_TUTORIAL_STARTED,
+ HUB_MODE_TUTORIAL_COMPLETED
+ })
+ public @interface HubModeTutorialState {
+ }
+
+ /**
+ * Defines the user's current state of navigating through the hub mode tutorial.
+ * The possible states are defined in {@link HubModeTutorialState}.
+ *
+ * @hide
+ */
+ public static final String HUB_MODE_TUTORIAL_STATE = "hub_mode_tutorial_state";
+
/**
* The default NFC payment component
* @hide
@@ -11105,6 +11147,12 @@
public static final String BLUETOOTH_ON_WHILE_DRIVING = "bluetooth_on_while_driving";
/**
+ * Volume dialog timeout in ms.
+ * @hide
+ */
+ public static final String VOLUME_DIALOG_DISMISS_TIMEOUT = "volume_dialog_dismiss_timeout";
+
+ /**
* What behavior should be invoked when the volume hush gesture is triggered
* One of VOLUME_HUSH_OFF, VOLUME_HUSH_VIBRATE, VOLUME_HUSH_MUTE.
*
diff --git a/core/java/android/service/notification/TEST_MAPPING b/core/java/android/service/notification/TEST_MAPPING
index 7b8d52f..468c451 100644
--- a/core/java/android/service/notification/TEST_MAPPING
+++ b/core/java/android/service/notification/TEST_MAPPING
@@ -4,9 +4,6 @@
"name": "CtsNotificationTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
@@ -21,9 +18,6 @@
"name": "FrameworksUiServicesTests",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
diff --git a/core/java/android/text/TEST_MAPPING b/core/java/android/text/TEST_MAPPING
index 0fe974a..c9bd2ca 100644
--- a/core/java/android/text/TEST_MAPPING
+++ b/core/java/android/text/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsTextTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"exclude-annotation": "androidx.test.filters.LargeTest"
diff --git a/core/java/android/text/flags/fix_double_underline.aconfig b/core/java/android/text/flags/fix_double_underline.aconfig
new file mode 100644
index 0000000..b0aa72a
--- /dev/null
+++ b/core/java/android/text/flags/fix_double_underline.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.text.flags"
+
+flag {
+ name: "fix_double_underline"
+ namespace: "text"
+ description: "Feature flag for fixing double underline because of the multiple font used in the single line."
+ bug: "297336724"
+}
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index bdd0a9c..e4e8b7b5b 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -30,8 +31,10 @@
import android.graphics.TextureLayer;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.flags.Flags;
/**
* <p>A TextureView can be used to display a content stream, such as that
@@ -51,9 +54,9 @@
* <th style="text-align: center;">SurfaceView</th>
* </tr>
* <tr>
- * <td>Supports alpha</td>
+ * <td>Supports View alpha</td>
* <td style="text-align: center;">X</td>
- * <td style="text-align: center;"> </td>
+ * <td style="text-align: center;">U+</td>
* </tr>
* <tr>
* <td>Supports rotations</td>
@@ -194,6 +197,9 @@
private Canvas mCanvas;
private int mSaveCount;
+ @FloatRange(from = 0.0) float mFrameRate;
+ @Surface.FrameRateCompatibility int mFrameRateCompatibility;
+
private final Object[] mNativeWindowLock = new Object[0];
// Set by native code, do not write!
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -465,6 +471,16 @@
mLayer.setSurfaceTexture(mSurface);
mSurface.setDefaultBufferSize(getWidth(), getHeight());
mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
+ if (Flags.toolkitSetFrameRate()) {
+ mSurface.setOnSetFrameRateListener(
+ (surfaceTexture, frameRate, compatibility, strategy) -> {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.instant(Trace.TRACE_TAG_VIEW, "setFrameRate: " + frameRate);
+ }
+ mFrameRate = frameRate;
+ mFrameRateCompatibility = compatibility;
+ }, mAttachInfo.mHandler);
+ }
if (mListener != null && createNewSurface) {
mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 55374b9..7674131 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1328,6 +1328,14 @@
public static final String AUTOFILL_HINT_PASSWORD_AUTO = "passwordAuto";
/**
+ * Hint indicating that the developer intends to fill this view with output from
+ * CredentialManager.
+ *
+ * @hide
+ */
+ public static final String AUTOFILL_HINT_CREDENTIAL_MANAGER = "credential";
+
+ /**
* Hints for the autofill services that describes the content of the view.
*/
private @Nullable String[] mAutofillHints;
@@ -8562,6 +8570,15 @@
* announcements every time a View is updated.
*
* <p>
+ * For notifying users about errors, such as in a login screen with text that displays an
+ * "incorrect password" notification, that view should send an AccessibilityEvent of type
+ * {@link AccessibilityEvent#CONTENT_CHANGE_TYPE_ERROR} and set
+ * {@link AccessibilityNodeInfo#setError(CharSequence)} instead. Custom widgets should expose
+ * error-setting methods that support accessibility automatically. For example, instead of
+ * explicitly sending this event when using a TextView, use
+ * {@link android.widget.TextView#setError(CharSequence)}.
+ *
+ * <p>
* Use {@link #setStateDescription(CharSequence)} to convey state changes to views within the
* user interface. While a live region may send different types of events generated by the view,
* state description will send {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events of
@@ -32618,6 +32635,7 @@
* @see android.view.inputmethod.InputMethodManager#startStylusHandwriting(View)
* @param enabled whether auto handwriting initiation is enabled for this view.
* @attr ref android.R.styleable#View_autoHandwritingEnabled
+ * @see EditorInfo#setStylusHandwritingEnabled(boolean)
*/
public void setAutoHandwritingEnabled(boolean enabled) {
if (enabled) {
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 8ba8b8c..a07b62f 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -16,7 +16,11 @@
package android.view.animation;
+import static android.view.flags.Flags.FLAG_EXPECTED_PRESENTATION_TIME_API;
+import static android.view.flags.Flags.expectedPresentationTimeApi;
+
import android.annotation.AnimRes;
+import android.annotation.FlaggedApi;
import android.annotation.InterpolatorRes;
import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
@@ -151,7 +155,12 @@
* @return the expected presentation time of a frame in the
* {@link System#nanoTime()} time base.
*/
+ @FlaggedApi(FLAG_EXPECTED_PRESENTATION_TIME_API)
public static long getExpectedPresentationTimeNanos() {
+ if (!expectedPresentationTimeApi()) {
+ return SystemClock.uptimeMillis();
+ }
+
AnimationState state = sAnimationState.get();
return state.mExpectedPresentationTimeNanos;
}
@@ -164,6 +173,7 @@
* @return the expected presentation time of a frame in the
* {@link SystemClock#uptimeMillis()} time base.
*/
+ @FlaggedApi(FLAG_EXPECTED_PRESENTATION_TIME_API)
public static long getExpectedPresentationTimeMillis() {
return getExpectedPresentationTimeNanos() / TimeUtils.NANOS_PER_MS;
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 4cb8788..6cf185a 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1464,7 +1464,7 @@
if (infos.size() == 0) {
throw new IllegalArgumentException("No VirtualViewInfo found");
}
- if (view.isCredential() && mIsFillAndSaveDialogDisabledForCredentialManager) {
+ if (isCredmanRequested(view) && mIsFillAndSaveDialogDisabledForCredentialManager) {
if (sDebug) {
Log.d(TAG, "Ignoring Fill Dialog request since important for credMan:"
+ view.getAutofillId().toString());
@@ -1488,7 +1488,7 @@
* @hide
*/
public void notifyViewEnteredForFillDialog(View v) {
- if (v.isCredential()
+ if (isCredmanRequested(v)
&& mIsFillAndSaveDialogDisabledForCredentialManager) {
if (sDebug) {
Log.d(TAG, "Ignoring Fill Dialog request since important for credMan:"
@@ -3434,6 +3434,22 @@
}
}
+ private boolean isCredmanRequested(View view) {
+ if (view.isCredential()) {
+ return true;
+ }
+ String[] hints = view.getAutofillHints();
+ if (hints == null) {
+ return false;
+ }
+ for (String hint : hints) {
+ if (hint.equals(View.AUTOFILL_HINT_CREDENTIAL_MANAGER)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Find a single view by its id.
*
diff --git a/core/java/android/view/flags/variable_refresh_rate_flags.aconfig b/core/java/android/view/flags/variable_refresh_rate_flags.aconfig
index e3972ad..13a6f8d 100644
--- a/core/java/android/view/flags/variable_refresh_rate_flags.aconfig
+++ b/core/java/android/view/flags/variable_refresh_rate_flags.aconfig
@@ -12,4 +12,11 @@
namespace: "toolkit"
description: "Feature flag for toolkit to set frame rate"
bug: "293512962"
+}
+
+flag {
+ name: "expected_presentation_time_api"
+ namespace: "toolkit"
+ description: "Feature flag for using expected presentation time of the Choreographer"
+ bug: "278730197"
}
\ No newline at end of file
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 4e5cec7..a92420a 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -23,7 +23,9 @@
import static android.view.inputmethod.EditorInfoProto.PACKAGE_NAME;
import static android.view.inputmethod.EditorInfoProto.PRIVATE_IME_OPTIONS;
import static android.view.inputmethod.EditorInfoProto.TARGET_INPUT_METHOD_USER_ID;
+import static android.view.inputmethod.Flags.FLAG_EDITORINFO_HANDWRITING_ENABLED;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -45,6 +47,7 @@
import android.view.MotionEvent.ToolType;
import android.view.View;
import android.view.autofill.AutofillId;
+import android.widget.Editor;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -716,6 +719,33 @@
return set;
}
+ private boolean mIsStylusHandwritingEnabled;
+
+ /**
+ * Set {@code true} if the {@link Editor} has
+ * {@link InputMethodManager#startStylusHandwriting stylus handwriting} enabled.
+ * {@code false} by default, {@link Editor} must set it {@code true} to indicate that
+ * it supports stylus handwriting.
+ *
+ * @param enabled {@code true} if stylus handwriting is enabled.
+ * @see View#setAutoHandwritingEnabled(boolean)
+ */
+ @FlaggedApi(FLAG_EDITORINFO_HANDWRITING_ENABLED)
+ public void setStylusHandwritingEnabled(boolean enabled) {
+ mIsStylusHandwritingEnabled = enabled;
+ }
+
+ /**
+ * Returns {@code true} when an {@link Editor} has stylus handwriting enabled.
+ * {@code false} by default.
+ * @see #setStylusHandwritingEnabled(boolean)
+ * @see InputMethodManager#isStylusHandwritingAvailable()
+ */
+ @FlaggedApi(FLAG_EDITORINFO_HANDWRITING_ENABLED)
+ public boolean isStylusHandwritingEnabled() {
+ return mIsStylusHandwritingEnabled;
+ }
+
/**
* If not {@code null}, this editor needs to talk to IMEs that run for the specified user, no
* matter what user ID the calling process has.
@@ -1211,6 +1241,7 @@
pw.println(prefix + "supportedHandwritingGesturePreviewTypes="
+ InputMethodDebug.handwritingGestureTypeFlagsToString(
mSupportedHandwritingGesturePreviewTypes));
+ pw.println(prefix + "isStylusHandwritingEnabled=" + mIsStylusHandwritingEnabled);
pw.println(prefix + "contentMimeTypes=" + Arrays.toString(contentMimeTypes));
if (targetInputMethodUser != null) {
pw.println(prefix + "targetInputMethodUserId=" + targetInputMethodUser.getIdentifier());
@@ -1277,6 +1308,9 @@
dest.writeBundle(extras);
dest.writeInt(mSupportedHandwritingGestureTypes);
dest.writeInt(mSupportedHandwritingGesturePreviewTypes);
+ if (Flags.editorinfoHandwritingEnabled()) {
+ dest.writeBoolean(mIsStylusHandwritingEnabled);
+ }
dest.writeBoolean(mInitialSurroundingText != null);
if (mInitialSurroundingText != null) {
mInitialSurroundingText.writeToParcel(dest, flags);
@@ -1316,6 +1350,9 @@
res.extras = source.readBundle();
res.mSupportedHandwritingGestureTypes = source.readInt();
res.mSupportedHandwritingGesturePreviewTypes = source.readInt();
+ if (Flags.editorinfoHandwritingEnabled()) {
+ res.mIsStylusHandwritingEnabled = source.readBoolean();
+ }
boolean hasInitialSurroundingText = source.readBoolean();
if (hasInitialSurroundingText) {
res.mInitialSurroundingText =
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index c144289..c14b510 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -6,4 +6,12 @@
description: "Feature flag for refactoring InsetsController and removing ImeInsetsSourceConsumer"
bug: "298172246"
is_fixed_read_only: true
+}
+
+flag {
+ name: "editorinfo_handwriting_enabled"
+ namespace: "input_method"
+ description: "Feature flag for adding EditorInfo#mStylusHandwritingEnabled"
+ bug: "293898187"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index d25c8a8..7b7e341 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -25,9 +25,12 @@
interface ITaskFragmentOrganizerController {
/**
- * Registers a TaskFragmentOrganizer to manage TaskFragments.
+ * Registers a TaskFragmentOrganizer to manage TaskFragments. Registering a system
+ * organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer will have additional
+ * system capabilities.
*/
- void registerOrganizer(in ITaskFragmentOrganizer organizer);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true)")
+ void registerOrganizer(in ITaskFragmentOrganizer organizer, in boolean isSystemOrganizer);
/**
* Unregisters a previously registered TaskFragmentOrganizer.
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index a88e394..451acbe 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -122,7 +122,7 @@
TYPE_PARAMETER_PROCESS_RUNNING,
TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT,
TYPE_PARAMETER_ACTIVITY_CREATED,
- TYPE_PARAMETER_ALLOW_ICON,
+ TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN,
TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN,
TYPE_PARAMETER_WINDOWLESS,
TYPE_PARAMETER_LEGACY_SPLASH_SCREEN
@@ -143,7 +143,7 @@
/** @hide */
public static final int TYPE_PARAMETER_ACTIVITY_CREATED = 0x00000010;
/** @hide */
- public static final int TYPE_PARAMETER_ALLOW_ICON = 0x00000020;
+ public static final int TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN = 0x00000020;
/**
* The parameter which indicates if the activity has finished drawing.
* @hide
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index f785a3d..a6c9cec 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -22,9 +22,11 @@
import static android.view.WindowManager.TRANSIT_OPEN;
import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.os.Bundle;
import android.os.IBinder;
@@ -32,6 +34,8 @@
import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
+import com.android.window.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
@@ -140,12 +144,34 @@
}
/**
- * Registers a TaskFragmentOrganizer to manage TaskFragments.
+ * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments.
*/
@CallSuper
public void registerOrganizer() {
+ // TODO(b/302420256) point to registerOrganizer(boolean) when flag is removed.
try {
- getController().registerOrganizer(mInterface);
+ getController().registerOrganizer(mInterface, false /* isSystemOrganizer */);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments.
+ *
+ * Registering a system organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer
+ * will have additional system capabilities, including: (1) it will receive SurfaceControl for
+ * the organized TaskFragment, and (2) it needs to update the
+ * {@link android.view.SurfaceControl} following the window change accordingly.
+ *
+ * @hide
+ */
+ @CallSuper
+ @RequiresPermission(value = "android.permission.MANAGE_ACTIVITY_TASKS", conditional = true)
+ @FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG)
+ public void registerOrganizer(boolean isSystemOrganizer) {
+ try {
+ getController().registerOrganizer(mInterface, isSystemOrganizer);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java
index 3c5d60d..4dada10 100644
--- a/core/java/android/window/TaskFragmentTransaction.java
+++ b/core/java/android/window/TaskFragmentTransaction.java
@@ -30,6 +30,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.SurfaceControl;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -192,6 +193,9 @@
@Nullable
private TaskFragmentParentInfo mTaskFragmentParentInfo;
+ @Nullable
+ private SurfaceControl mSurfaceControl;
+
public Change(@ChangeType int type) {
mType = type;
}
@@ -206,6 +210,7 @@
mActivityIntent = in.readTypedObject(Intent.CREATOR);
mActivityToken = in.readStrongBinder();
mTaskFragmentParentInfo = in.readTypedObject(TaskFragmentParentInfo.CREATOR);
+ mSurfaceControl = in.readTypedObject(SurfaceControl.CREATOR);
}
@Override
@@ -219,6 +224,7 @@
dest.writeTypedObject(mActivityIntent, flags);
dest.writeStrongBinder(mActivityToken);
dest.writeTypedObject(mTaskFragmentParentInfo, flags);
+ dest.writeTypedObject(mSurfaceControl, flags);
}
/** The change is related to the TaskFragment created with this unique token. */
@@ -306,6 +312,13 @@
return this;
}
+ /** @hide */
+ @NonNull
+ public Change setTaskFragmentSurfaceControl(@Nullable SurfaceControl sc) {
+ mSurfaceControl = sc;
+ return this;
+ }
+
@ChangeType
public int getType() {
return mType;
@@ -359,6 +372,21 @@
return mTaskFragmentParentInfo;
}
+ /**
+ * Gets the {@link SurfaceControl} of the TaskFragment. This field is {@code null} for
+ * a regular {@link TaskFragmentOrganizer} and is only available for a system
+ * {@link TaskFragmentOrganizer} in the
+ * {@link TaskFragmentTransaction#TYPE_TASK_FRAGMENT_APPEARED} event. See
+ * {@link ITaskFragmentOrganizerController#registerOrganizer(ITaskFragmentOrganizer,
+ * boolean)}
+ *
+ * @hide
+ */
+ @Nullable
+ public SurfaceControl getTaskFragmentSurfaceControl() {
+ return mSurfaceControl;
+ }
+
@Override
public String toString() {
return "Change{ type=" + mType + " }";
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index 7a87c3a..d503904 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -16,11 +16,9 @@
package com.android.internal.display;
-import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
-import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.net.Uri;
@@ -56,7 +54,8 @@
private static final int MSG_RUN_UPDATE = 1;
// The tolerance within which we consider brightness values approximately equal to eachother.
- public static final float EPSILON = 0.0001f;
+ // This value is approximately 1/3 of the smallest possible brightness value.
+ public static final float EPSILON = 0.001f;
private static int sBrightnessUpdateCount = 1;
@@ -285,74 +284,6 @@
}
/**
- * Converts between the int brightness setting and the float brightness system. The int
- * brightness setting is between 0-255 and matches the brightness slider - e.g. 128 is 50% on
- * the slider. Accounts for special values such as OFF and invalid values. Accounts for
- * brightness limits; the maximum value here represents the max value allowed on the slider.
- */
- @VisibleForTesting
- @SuppressLint("AndroidFrameworkRequiresPermission")
- public float brightnessIntSettingToFloat(int brightnessInt) {
- if (brightnessInt == PowerManager.BRIGHTNESS_OFF) {
- return PowerManager.BRIGHTNESS_OFF_FLOAT;
- } else if (brightnessInt == PowerManager.BRIGHTNESS_INVALID) {
- return PowerManager.BRIGHTNESS_INVALID_FLOAT;
- } else {
- final float minInt = PowerManager.BRIGHTNESS_OFF + 1;
- final float maxInt = PowerManager.BRIGHTNESS_ON;
-
- // Normalize to the range [0, 1]
- float userPerceptionBrightness = MathUtils.norm(minInt, maxInt, brightnessInt);
-
- // Convert from user-perception to linear scale
- float linearBrightness = BrightnessUtils.convertGammaToLinear(userPerceptionBrightness);
-
- // Interpolate to the range [0, currentlyAllowedMax]
- final Display display = mContext.getDisplay();
- if (display == null) {
- return PowerManager.BRIGHTNESS_INVALID_FLOAT;
- }
- final BrightnessInfo info = display.getBrightnessInfo();
- return MathUtils.lerp(info.brightnessMinimum, info.brightnessMaximum, linearBrightness);
- }
- }
-
- /**
- * Translates specified value from the float brightness system to the setting int brightness
- * system. The value returned is between 0-255 and matches the brightness slider - e.g. 128 is
- * 50% on the slider. Accounts for special values such as OFF and invalid values. Accounts for
- * brightness limits; the maximum value here represents the max value currently allowed on
- * the slider.
- */
- @VisibleForTesting
- @SuppressLint("AndroidFrameworkRequiresPermission")
- public int brightnessFloatToIntSetting(float brightnessFloat) {
- if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) {
- return PowerManager.BRIGHTNESS_OFF;
- } else if (Float.isNaN(brightnessFloat)) {
- return PowerManager.BRIGHTNESS_INVALID;
- } else {
- // Normalize to the range [0, 1]
- final Display display = mContext.getDisplay();
- if (display == null) {
- return PowerManager.BRIGHTNESS_INVALID;
- }
- final BrightnessInfo info = display.getBrightnessInfo();
- float linearBrightness =
- MathUtils.norm(info.brightnessMinimum, info.brightnessMaximum, brightnessFloat);
-
- // Convert from linear to user-perception scale
- float userPerceptionBrightness = BrightnessUtils.convertLinearToGamma(linearBrightness);
-
- // Interpolate to the range [0, 255]
- final float minInt = PowerManager.BRIGHTNESS_OFF + 1;
- final float maxInt = PowerManager.BRIGHTNESS_ON;
- float intBrightness = MathUtils.lerp(minInt, maxInt, userPerceptionBrightness);
- return Math.round(intBrightness);
- }
- }
-
- /**
* Encapsulates a brightness change event and contains logic for synchronizing the appropriate
* settings for the specified brightness change.
*/
@@ -490,14 +421,14 @@
if (mSourceType == TYPE_INT) {
return (int) mBrightness;
}
- return brightnessFloatToIntSetting(mBrightness);
+ return brightnessFloatToInt(mBrightness);
}
private float getBrightnessAsFloat() {
if (mSourceType == TYPE_FLOAT) {
return mBrightness;
}
- return brightnessIntSettingToFloat((int) mBrightness);
+ return brightnessIntToFloat((int) mBrightness);
}
private String toStringLabel(int type, float brightness) {
diff --git a/core/java/com/android/internal/infra/TEST_MAPPING b/core/java/com/android/internal/infra/TEST_MAPPING
index ddfd0ed..c09181f 100644
--- a/core/java/com/android/internal/infra/TEST_MAPPING
+++ b/core/java/com/android/internal/infra/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsRoleTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -15,7 +15,7 @@
"include-filter": "android.permission.cts.PermissionControllerTest"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
diff --git a/core/java/com/android/internal/net/OWNERS b/core/java/com/android/internal/net/OWNERS
index 71f997b..7157683 100644
--- a/core/java/com/android/internal/net/OWNERS
+++ b/core/java/com/android/internal/net/OWNERS
@@ -1,4 +1,4 @@
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
jsharkey@android.com
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 7eeac29..4d8eeac 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -316,7 +316,14 @@
*/
void requestTileServiceListeningState(in ComponentName componentName);
- void requestAddTile(in ComponentName componentName, in CharSequence appName, in CharSequence label, in Icon icon, in IAddTileResultCallback callback);
+ void requestAddTile(
+ int callingUid,
+ in ComponentName componentName,
+ in CharSequence appName,
+ in CharSequence label,
+ in Icon icon,
+ in IAddTileResultCallback callback
+ );
void cancelRequestAddTile(in String packageName);
/** Notifies System UI about an update to the media tap-to-transfer sender state. */
diff --git a/core/java/com/android/server/net/OWNERS b/core/java/com/android/server/net/OWNERS
index 62c5737..c24680e9 100644
--- a/core/java/com/android/server/net/OWNERS
+++ b/core/java/com/android/server/net/OWNERS
@@ -1,2 +1,2 @@
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 9ed4155..3795fc8 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -102,6 +102,7 @@
static_libs: [
"libnativehelper_lazy",
"libziparchive_for_incfs",
+ "libguiflags",
],
export_include_dirs: [
diff --git a/core/jni/android_graphics_SurfaceTexture.cpp b/core/jni/android_graphics_SurfaceTexture.cpp
index 21487ab..50832a5 100644
--- a/core/jni/android_graphics_SurfaceTexture.cpp
+++ b/core/jni/android_graphics_SurfaceTexture.cpp
@@ -17,27 +17,24 @@
#undef LOG_TAG
#define LOG_TAG "SurfaceTexture"
-#include <stdio.h>
-
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
-
+#include <com_android_graphics_libgui_flags.h>
+#include <cutils/atomic.h>
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <stdio.h>
#include <surfacetexture/SurfaceTexture.h>
#include <surfacetexture/surface_texture_platform.h>
-
-#include "core_jni_helpers.h"
-
-#include <cutils/atomic.h>
#include <utils/Log.h>
#include <utils/misc.h>
+#include "core_jni_helpers.h"
#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
// ----------------------------------------------------------------------------
@@ -55,6 +52,7 @@
jfieldID producer;
jfieldID frameAvailableListener;
jmethodID postEvent;
+ jmethodID postOnSetFrameRateEvent;
};
static fields_t fields;
@@ -139,61 +137,81 @@
// ----------------------------------------------------------------------------
-class JNISurfaceTextureContext : public SurfaceTexture::FrameAvailableListener
-{
+class JNISurfaceTextureContextCommon {
public:
- JNISurfaceTextureContext(JNIEnv* env, jobject weakThiz, jclass clazz);
- virtual ~JNISurfaceTextureContext();
- virtual void onFrameAvailable(const BufferItem& item);
+ JNISurfaceTextureContextCommon(JNIEnv* env, jobject weakThiz, jclass clazz)
+ : mWeakThiz(env->NewGlobalRef(weakThiz)), mClazz((jclass)env->NewGlobalRef(clazz)) {}
-private:
- static JNIEnv* getJNIEnv();
+ virtual ~JNISurfaceTextureContextCommon() {
+ JNIEnv* env = getJNIEnv();
+ if (env != NULL) {
+ env->DeleteGlobalRef(mWeakThiz);
+ env->DeleteGlobalRef(mClazz);
+ } else {
+ ALOGW("leaking JNI object references");
+ }
+ }
+
+ void onFrameAvailable(const BufferItem& item) {
+ JNIEnv* env = getJNIEnv();
+ if (env != NULL) {
+ env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
+ } else {
+ ALOGW("onFrameAvailable event will not posted");
+ }
+ }
+
+protected:
+ static JNIEnv* getJNIEnv() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (env == NULL) {
+ JavaVMAttachArgs args = {JNI_VERSION_1_4, "JNISurfaceTextureContext", NULL};
+ JavaVM* vm = AndroidRuntime::getJavaVM();
+ int result = vm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
+ if (result != JNI_OK) {
+ ALOGE("thread attach failed: %#x", result);
+ return NULL;
+ }
+ }
+ return env;
+ }
jobject mWeakThiz;
jclass mClazz;
};
-JNISurfaceTextureContext::JNISurfaceTextureContext(JNIEnv* env,
- jobject weakThiz, jclass clazz) :
- mWeakThiz(env->NewGlobalRef(weakThiz)),
- mClazz((jclass)env->NewGlobalRef(clazz))
-{}
+class JNISurfaceTextureContextFrameAvailableListener
+ : public JNISurfaceTextureContextCommon,
+ public SurfaceTexture::FrameAvailableListener {
+public:
+ JNISurfaceTextureContextFrameAvailableListener(JNIEnv* env, jobject weakThiz, jclass clazz)
+ : JNISurfaceTextureContextCommon(env, weakThiz, clazz) {}
+ void onFrameAvailable(const BufferItem& item) override {
+ JNISurfaceTextureContextCommon::onFrameAvailable(item);
+ }
+};
-JNIEnv* JNISurfaceTextureContext::getJNIEnv() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- JavaVMAttachArgs args = {
- JNI_VERSION_1_4, "JNISurfaceTextureContext", NULL };
- JavaVM* vm = AndroidRuntime::getJavaVM();
- int result = vm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
- if (result != JNI_OK) {
- ALOGE("thread attach failed: %#x", result);
- return NULL;
+class JNISurfaceTextureContextListener : public JNISurfaceTextureContextCommon,
+ public SurfaceTexture::SurfaceTextureListener {
+public:
+ JNISurfaceTextureContextListener(JNIEnv* env, jobject weakThiz, jclass clazz)
+ : JNISurfaceTextureContextCommon(env, weakThiz, clazz) {}
+
+ void onFrameAvailable(const BufferItem& item) override {
+ JNISurfaceTextureContextCommon::onFrameAvailable(item);
+ }
+
+ void onSetFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) override {
+ JNIEnv* env = getJNIEnv();
+ if (env != NULL) {
+ env->CallStaticVoidMethod(mClazz, fields.postOnSetFrameRateEvent, mWeakThiz, frameRate,
+ compatibility, changeFrameRateStrategy);
+ } else {
+ ALOGW("onSetFrameRate event will not posted");
}
}
- return env;
-}
-
-JNISurfaceTextureContext::~JNISurfaceTextureContext()
-{
- JNIEnv* env = getJNIEnv();
- if (env != NULL) {
- env->DeleteGlobalRef(mWeakThiz);
- env->DeleteGlobalRef(mClazz);
- } else {
- ALOGW("leaking JNI object references");
- }
-}
-
-void JNISurfaceTextureContext::onFrameAvailable(const BufferItem& /* item */)
-{
- JNIEnv* env = getJNIEnv();
- if (env != NULL) {
- env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
- } else {
- ALOGW("onFrameAvailable event will not posted");
- }
-}
+};
// ----------------------------------------------------------------------------
@@ -229,6 +247,13 @@
if (fields.postEvent == NULL) {
ALOGE("can't find android/graphics/SurfaceTexture.postEventFromNative");
}
+
+ fields.postOnSetFrameRateEvent =
+ env->GetStaticMethodID(clazz, "postOnSetFrameRateEventFromNative",
+ "(Ljava/lang/ref/WeakReference;FII)V");
+ if (fields.postOnSetFrameRateEvent == NULL) {
+ ALOGE("can't find android/graphics/SurfaceTexture.postOnSetFrameRateEventFromNative");
+ }
}
static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached,
@@ -274,17 +299,27 @@
return;
}
- sp<JNISurfaceTextureContext> ctx(new JNISurfaceTextureContext(env, weakThiz,
- clazz));
- surfaceTexture->setFrameAvailableListener(ctx);
- SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);
+ if (com::android::graphics::libgui::flags::bq_setframerate()) {
+ sp<JNISurfaceTextureContextListener> ctx(
+ new JNISurfaceTextureContextListener(env, weakThiz, clazz));
+ surfaceTexture->setSurfaceTextureListener(ctx);
+ } else {
+ sp<JNISurfaceTextureContextFrameAvailableListener> ctx(
+ new JNISurfaceTextureContextFrameAvailableListener(env, weakThiz, clazz));
+ surfaceTexture->setFrameAvailableListener(ctx);
+ SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);
+ }
}
static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz)
{
sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
- surfaceTexture->setFrameAvailableListener(0);
- SurfaceTexture_setFrameAvailableListener(env, thiz, 0);
+ if (com::android::graphics::libgui::flags::bq_setframerate()) {
+ surfaceTexture->setSurfaceTextureListener(0);
+ } else {
+ surfaceTexture->setFrameAvailableListener(0);
+ SurfaceTexture_setFrameAvailableListener(env, thiz, 0);
+ }
SurfaceTexture_setSurfaceTexture(env, thiz, 0);
SurfaceTexture_setProducer(env, thiz, 0);
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c00a776f..4e073ca 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4830,7 +4830,7 @@
<string translatable="false" name="config_deviceSpecificInputMethodManagerService"></string>
<!-- Component name of media projection permission dialog -->
- <string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
+ <string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.mediaprojection.permission.MediaProjectionPermissionActivity</string>
<!-- Corner radius of system dialogs -->
<dimen name="config_dialogCornerRadius">28dp</dimen>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 878e6b3..71d696e 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -172,28 +172,17 @@
<integer name="config_satellite_nb_iot_inactivity_timeout_millis">180000</integer>
<java-symbol type="integer" name="config_satellite_nb_iot_inactivity_timeout_millis" />
- <!-- Telephony config for services supported by satellite providers. The format of each config
- string in the array is as follows: "PLMN_1:service_1,service_2,..."
- where PLMN is the satellite PLMN of a provider and service is an integer with the
- following value:
- 1 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VOICE}
- 2 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_DATA}
- 3 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_SMS}
- 4 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VIDEO}
- 5 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_EMERGENCY}
- 6 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_MMS}
- Example of a config string: "10011:2,3"
+ <!-- Telephony config for the PLMNs of all satellite providers. This is used by satellite modem
+ to identify providers that should be ignored if the carrier config
+ carrier_supported_satellite_services_per_provider_bundle does not support them.
+ -->
+ <string-array name="config_satellite_providers" translatable="false"></string-array>
+ <java-symbol type="array" name="config_satellite_providers" />
- The PLMNs not configured in this array will be ignored and will not be used for satellite
- scanning. -->
- <string-array name="config_satellite_services_supported_by_providers" translatable="false">
- </string-array>
- <java-symbol type="array" name="config_satellite_services_supported_by_providers" />
-
- <!-- The identifier of the satellite's eSIM profile preloaded on the device. The identifier is
- composed of MCC and MNC of the satellite PLMN with the format "mccmnc". -->
- <string name="config_satellite_esim_identifier" translatable="false"></string>
- <java-symbol type="string" name="config_satellite_esim_identifier" />
+ <!-- The identifier of the satellite's SIM profile. The identifier is composed of MCC and MNC
+ of the satellite PLMN with the format "mccmnc". -->
+ <string name="config_satellite_sim_identifier" translatable="false"></string>
+ <java-symbol type="string" name="config_satellite_sim_identifier" />
<!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks
will not perform handover if the target transport is out of service, or VoPS not
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 48dc167..7f3e014 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -69,6 +69,7 @@
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
+import android.compat.testing.PlatformCompatChangeRule;
import android.content.Context;
import android.content.Intent;
import android.content.LocusId;
@@ -95,16 +96,20 @@
import android.widget.RemoteViews;
import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.internal.util.ContrastColorUtil;
import junit.framework.Assert;
+import libcore.junit.util.compat.CoreCompatChangeRule;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import java.util.List;
@@ -116,6 +121,9 @@
private Context mContext;
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
@Before
public void setUp() {
mContext = InstrumentationRegistry.getContext();
@@ -1777,6 +1785,42 @@
assertThat(recoveredExtender.getColor()).isEqualTo(1234);
}
+ @Test
+ @CoreCompatChangeRule.EnableCompatChanges({Notification.WEARABLE_EXTENDER_BACKGROUND_BLOCKED})
+ public void wearableBackgroundBlockEnabled_wearableBackgroundSet_valueRemainsNull() {
+ Notification.WearableExtender extender = new Notification.WearableExtender();
+ Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+ extender.setBackground(bitmap);
+ Notification notif =
+ new Notification.Builder(mContext, "test id")
+ .setSmallIcon(1)
+ .setContentTitle("test_title")
+ .extend(extender)
+ .build();
+
+ Notification.WearableExtender result = new Notification.WearableExtender(notif);
+ Assert.assertNull(result.getBackground());
+ }
+
+ @Test
+ @CoreCompatChangeRule.DisableCompatChanges({Notification.WEARABLE_EXTENDER_BACKGROUND_BLOCKED})
+ public void wearableBackgroundBlockDisabled_wearableBackgroundSet_valueKeepsBitmap() {
+ Notification.WearableExtender extender = new Notification.WearableExtender();
+ Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+ extender.setBackground(bitmap);
+ Notification notif =
+ new Notification.Builder(mContext, "test id")
+ .setSmallIcon(1)
+ .setContentTitle("test_title")
+ .extend(extender)
+ .build();
+
+ Notification.WearableExtender result = new Notification.WearableExtender(notif);
+ Bitmap resultBitmap = result.getBackground();
+ assertNotNull(resultBitmap);
+ Assert.assertEquals(bitmap, resultBitmap);
+ }
+
private void assertValid(Notification.Colors c) {
// Assert that all colors are populated
assertThat(c.getBackgroundColor()).isNotEqualTo(Notification.COLOR_INVALID);
diff --git a/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java b/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java
new file mode 100644
index 0000000..ecd75a8
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityThread.ActivityClientRecord;
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link DestroyActivityItem}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:DestroyActivityItemTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class DestroyActivityItemTest {
+
+ @Mock
+ private ClientTransactionHandler mHandler;
+ @Mock
+ private PendingTransactionActions mPendingActions;
+ @Mock
+ private IBinder mActivityToken;
+
+ // Can't mock final class.
+ private ActivityClientRecord mActivityClientRecord;
+
+ private ArrayMap<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed;
+ private DestroyActivityItem mItem;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mItem = DestroyActivityItem.obtain(
+ mActivityToken, false /* finished */, 123 /* configChanges */);
+ mActivityClientRecord = new ActivityClientRecord();
+ mActivitiesToBeDestroyed = new ArrayMap<>();
+
+ doReturn(mActivitiesToBeDestroyed).when(mHandler).getActivitiesToBeDestroyed();
+ }
+
+ @Test
+ public void testPreExecute() {
+ mItem.preExecute(mHandler);
+
+ assertEquals(1, mActivitiesToBeDestroyed.size());
+ assertEquals(mItem, mActivitiesToBeDestroyed.get(mActivityToken));
+ }
+
+ @Test
+ public void testPostExecute() {
+ mItem.preExecute(mHandler);
+ mItem.postExecute(mHandler, mPendingActions);
+
+ assertTrue(mActivitiesToBeDestroyed.isEmpty());
+ }
+
+ @Test
+ public void testExecute() {
+ mItem.execute(mHandler, mActivityClientRecord, mPendingActions);
+
+ verify(mHandler).handleDestroyActivity(eq(mActivityClientRecord), eq(false) /* finishing */,
+ eq(123) /* configChanges */, eq(false) /* getNonConfigInstance */, any());
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index a1a2bdb..44a4d58 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -28,6 +28,7 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
@@ -247,7 +248,7 @@
@Test
public void testDoNotLaunchDestroyedActivity() {
- final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed = new ArrayMap<>();
+ final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
// Assume launch transaction is still in queue, so there is no client record.
when(mTransactionHandler.getActivityClient(any())).thenReturn(null);
@@ -259,7 +260,7 @@
DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */));
destroyTransaction.preExecute(mTransactionHandler);
// The activity should be added to to-be-destroyed container.
- assertEquals(1, mTransactionHandler.getActivitiesToBeDestroyed().size());
+ assertEquals(1, activitiesToBeDestroyed.size());
// A previous queued launch transaction runs on main thread (execute).
final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
@@ -274,7 +275,7 @@
// After the destroy transaction has been executed, the token should be removed.
mExecutor.execute(destroyTransaction);
- assertEquals(0, mTransactionHandler.getActivitiesToBeDestroyed().size());
+ assertTrue(activitiesToBeDestroyed.isEmpty());
}
@Test
diff --git a/core/tests/coretests/src/android/graphics/drawable/IconTest.java b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
index 5d92296..49ed3a8 100644
--- a/core/tests/coretests/src/android/graphics/drawable/IconTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
@@ -18,13 +18,20 @@
import static com.google.common.truth.Truth.assertThat;
+import android.app.IUriGrantsManager;
+import android.content.ContentProvider;
+import android.content.Intent;
+import android.content.pm.ParceledListSlice;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.RecordingCanvas;
import android.graphics.Region;
+import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Parcel;
+import android.os.RemoteException;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -34,6 +41,7 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
@@ -457,6 +465,81 @@
assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE);
}
+ @SmallTest
+ public void testLoadSafeDrawable_loadSuccessful() throws FileNotFoundException {
+ int uid = 12345;
+ String packageName = "test_pkg";
+
+ final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
+ .getBitmap();
+ final File dir = getContext().getExternalFilesDir(null);
+ final File file1 = new File(dir, "file1-original.png");
+ bit1.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(file1));
+
+ final Icon im1 = Icon.createWithFilePath(file1.toString());
+
+ TestableIUriGrantsManager ugm =
+ new TestableIUriGrantsManager(/* rejectCheckRequests */ false);
+
+ Drawable loadedDrawable = im1.loadDrawableCheckingUriGrant(
+ getContext(), ugm, uid, packageName);
+ assertThat(loadedDrawable).isNotNull();
+
+ assertThat(ugm.mRequests.size()).isEqualTo(1);
+ TestableIUriGrantsManager.CheckRequest r = ugm.mRequests.get(0);
+ assertThat(r.mCallingUid).isEqualTo(uid);
+ assertThat(r.mPackageName).isEqualTo(packageName);
+ assertThat(r.mMode).isEqualTo(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ assertThat(r.mUri).isEqualTo(ContentProvider.getUriWithoutUserId(im1.getUri()));
+ assertThat(r.mUserId).isEqualTo(ContentProvider.getUserIdFromUri(im1.getUri()));
+
+ final Bitmap test1 = Bitmap.createBitmap(loadedDrawable.getIntrinsicWidth(),
+ loadedDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ loadedDrawable.setBounds(0, 0, loadedDrawable.getIntrinsicWidth(),
+ loadedDrawable.getIntrinsicHeight());
+ loadedDrawable.draw(new Canvas(test1));
+
+ bit1.compress(Bitmap.CompressFormat.PNG, 100,
+ new FileOutputStream(new File(dir, "bitmap1-original.png")));
+ test1.compress(Bitmap.CompressFormat.PNG, 100,
+ new FileOutputStream(new File(dir, "bitmap1-test.png")));
+ if (!equalBitmaps(bit1, test1)) {
+ findBitmapDifferences(bit1, test1);
+ fail("bitmap1 differs, check " + dir);
+ }
+ }
+
+ @SmallTest
+ public void testLoadSafeDrawable_grantRejected_nullDrawable() throws FileNotFoundException {
+ int uid = 12345;
+ String packageName = "test_pkg";
+
+ final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
+ .getBitmap();
+ final File dir = getContext().getExternalFilesDir(null);
+ final File file1 = new File(dir, "file1-original.png");
+ bit1.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(file1));
+
+ final Icon im1 = Icon.createWithFilePath(file1.toString());
+
+ TestableIUriGrantsManager ugm =
+ new TestableIUriGrantsManager(/* rejectCheckRequests */ true);
+
+ Drawable loadedDrawable = im1.loadDrawableCheckingUriGrant(
+ getContext(), ugm, uid, packageName);
+
+ assertThat(ugm.mRequests.size()).isEqualTo(1);
+ TestableIUriGrantsManager.CheckRequest r = ugm.mRequests.get(0);
+ assertThat(r.mCallingUid).isEqualTo(uid);
+ assertThat(r.mPackageName).isEqualTo(packageName);
+ assertThat(r.mMode).isEqualTo(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ assertThat(r.mUri).isEqualTo(ContentProvider.getUriWithoutUserId(im1.getUri()));
+ assertThat(r.mUserId).isEqualTo(ContentProvider.getUserIdFromUri(im1.getUri()));
+
+ assertThat(loadedDrawable).isNull();
+ }
+
+
// ======== utils ========
static final char[] GRADIENT = " .:;+=xX$#".toCharArray();
@@ -541,4 +624,77 @@
}
L(sb.toString());
}
-}
+
+ private static class TestableIUriGrantsManager extends IUriGrantsManager.Stub {
+
+ final ArrayList<CheckRequest> mRequests = new ArrayList<>();
+ final boolean mRejectCheckRequests;
+
+ TestableIUriGrantsManager(boolean rejectCheckRequests) {
+ this.mRejectCheckRequests = rejectCheckRequests;
+ }
+
+ @Override
+ public void takePersistableUriPermission(Uri uri, int i, String s, int i1)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void releasePersistableUriPermission(Uri uri, int i, String s, int i1)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void grantUriPermissionFromOwner(IBinder iBinder, int i, String s, Uri uri, int i1,
+ int i2, int i3) throws RemoteException {
+
+ }
+
+ @Override
+ public ParceledListSlice getGrantedUriPermissions(String s, int i) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void clearGrantedUriPermissions(String s, int i) throws RemoteException {
+
+ }
+
+ @Override
+ public ParceledListSlice getUriPermissions(String s, boolean b, boolean b1)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public int checkGrantUriPermission_ignoreNonSystem(
+ int uid, String packageName, Uri uri, int mode, int userId)
+ throws RemoteException {
+ CheckRequest r = new CheckRequest(uid, packageName, uri, mode, userId);
+ mRequests.add(r);
+ if (mRejectCheckRequests) {
+ throw new SecurityException();
+ } else {
+ return uid;
+ }
+ }
+
+ static class CheckRequest {
+ final int mCallingUid;
+ final String mPackageName;
+ final Uri mUri;
+ final int mMode;
+ final int mUserId;
+
+ CheckRequest(int callingUid, String packageName, Uri uri, int mode, int userId) {
+ this.mCallingUid = callingUid;
+ this.mPackageName = packageName;
+ this.mUri = uri;
+ this.mMode = mode;
+ this.mUserId = userId;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index 6e73b9f..4839dd2 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -503,6 +503,7 @@
+ "prefix: hintLocales=null\n"
+ "prefix: supportedHandwritingGestureTypes=(none)\n"
+ "prefix: supportedHandwritingGesturePreviewTypes=(none)\n"
+ + "prefix: isStylusHandwritingEnabled=false\n"
+ "prefix: contentMimeTypes=null\n");
}
@@ -521,6 +522,9 @@
info.setSupportedHandwritingGestures(Arrays.asList(SelectGesture.class));
info.setSupportedHandwritingGesturePreviews(
Stream.of(SelectGesture.class).collect(Collectors.toSet()));
+ if (Flags.editorinfoHandwritingEnabled()) {
+ info.setStylusHandwritingEnabled(true);
+ }
info.packageName = "android.view.inputmethod";
info.autofillId = new AutofillId(123);
info.fieldId = 456;
@@ -544,6 +548,8 @@
+ "prefix2: hintLocales=[en,es,zh]\n"
+ "prefix2: supportedHandwritingGestureTypes=SELECT\n"
+ "prefix2: supportedHandwritingGesturePreviewTypes=SELECT\n"
+ + "prefix2: isStylusHandwritingEnabled="
+ + Flags.editorinfoHandwritingEnabled() + "\n"
+ "prefix2: contentMimeTypes=[image/png]\n"
+ "prefix2: targetInputMethodUserId=10\n");
}
@@ -565,6 +571,7 @@
+ "prefix: hintLocales=null\n"
+ "prefix: supportedHandwritingGestureTypes=(none)\n"
+ "prefix: supportedHandwritingGesturePreviewTypes=(none)\n"
+ + "prefix: isStylusHandwritingEnabled=false\n"
+ "prefix: contentMimeTypes=null\n");
}
diff --git a/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
index a8b4032..a5bbeb5 100644
--- a/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
+++ b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
@@ -17,6 +17,7 @@
package android.window.flags;
import static com.android.window.flags.Flags.syncWindowConfigUpdateFlag;
+import static com.android.window.flags.Flags.taskFragmentSystemOrganizerFlag;
import android.platform.test.annotations.Presubmit;
@@ -42,4 +43,10 @@
// No crash when accessing the flag.
syncWindowConfigUpdateFlag();
}
+
+ @Test
+ public void testTaskFragmentSystemOrganizerFlag() {
+ // No crash when accessing the flag.
+ taskFragmentSystemOrganizerFlag();
+ }
}
diff --git a/core/tests/vibrator/TEST_MAPPING b/core/tests/vibrator/TEST_MAPPING
index 2f3afa6..54a5ff1 100644
--- a/core/tests/vibrator/TEST_MAPPING
+++ b/core/tests/vibrator/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "FrameworksVibratorCoreTests",
"options": [
{"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index dfe5012..dd82fed 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.FloatRange;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
@@ -24,8 +25,10 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Trace;
import android.view.Surface;
import android.view.TextureView;
+import android.view.flags.Flags;
import java.lang.ref.WeakReference;
@@ -79,6 +82,7 @@
private final Looper mCreatorLooper;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Handler mOnFrameAvailableHandler;
+ private Handler mOnSetFrameRateHandler;
/**
* These fields are used by native code, do not access or modify.
@@ -100,6 +104,21 @@
}
/**
+ * Callback interface for being notified that a producer set a frame rate
+ * @hide
+ */
+ public interface OnSetFrameRateListener {
+ /**
+ * Called when the producer sets a frame rate
+ * @hide
+ */
+ void onSetFrameRate(SurfaceTexture surfaceTexture,
+ @FloatRange(from = 0.0) float frameRate,
+ @Surface.FrameRateCompatibility int compatibility,
+ @Surface.ChangeFrameRateStrategy int changeFrameRateStrategy);
+ }
+
+ /**
* Exception thrown when a SurfaceTexture couldn't be created or resized.
*
* @deprecated No longer thrown. {@link android.view.Surface.OutOfResourcesException}
@@ -224,6 +243,48 @@
}
}
+ private static class SetFrameRateArgs {
+ SetFrameRateArgs(@FloatRange(from = 0.0) float frameRate,
+ @Surface.FrameRateCompatibility int compatibility,
+ @Surface.ChangeFrameRateStrategy int changeFrameRateStrategy) {
+ this.mFrameRate = frameRate;
+ this.mCompatibility = compatibility;
+ this.mChangeFrameRateStrategy = changeFrameRateStrategy;
+ }
+ final float mFrameRate;
+ final int mCompatibility;
+ final int mChangeFrameRateStrategy;
+ }
+
+ /**
+ * Register a callback to be invoked when the producer sets a frame rate using
+ * Surface.setFrameRate.
+ * @hide
+ */
+ public void setOnSetFrameRateListener(@Nullable final OnSetFrameRateListener listener,
+ @Nullable Handler handler) {
+ if (listener != null) {
+ Looper looper = handler != null ? handler.getLooper() :
+ mCreatorLooper != null ? mCreatorLooper : Looper.getMainLooper();
+ mOnSetFrameRateHandler = new Handler(looper, null, true /*async*/) {
+ @Override
+ public void handleMessage(Message msg) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSetFrameRateHandler");
+ try {
+ SetFrameRateArgs args = (SetFrameRateArgs) msg.obj;
+ listener.onSetFrameRate(SurfaceTexture.this,
+ args.mFrameRate, args.mCompatibility,
+ args.mChangeFrameRateStrategy);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ }
+ };
+ } else {
+ mOnSetFrameRateHandler = null;
+ }
+ }
+
/**
* Set the default size of the image buffers. The image producer may override the buffer size,
* in which case the producer-set buffer size will be used, not the default size set by this
@@ -418,6 +479,35 @@
}
/**
+ * This method is invoked from native code only.
+ * @hide
+ */
+ @SuppressWarnings({"UnusedDeclaration"})
+ private static void postOnSetFrameRateEventFromNative(WeakReference<SurfaceTexture> weakSelf,
+ @FloatRange(from = 0.0) float frameRate,
+ @Surface.FrameRateCompatibility int compatibility,
+ @Surface.ChangeFrameRateStrategy int changeFrameRateStrategy) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "postOnSetFrameRateEventFromNative");
+ try {
+ if (Flags.toolkitSetFrameRate()) {
+ SurfaceTexture st = weakSelf.get();
+ if (st != null) {
+ Handler handler = st.mOnSetFrameRateHandler;
+ if (handler != null) {
+ Message msg = new Message();
+ msg.obj = new SetFrameRateArgs(frameRate, compatibility,
+ changeFrameRateStrategy);
+ handler.sendMessage(msg);
+ }
+ }
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+
+ }
+
+ /**
* Returns {@code true} if the SurfaceTexture is single-buffered.
* @hide
*/
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 708feeb..5509f00 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -24,9 +24,12 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.IUriGrantsManager;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
@@ -44,10 +47,13 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.RequiresPermission;
+
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
@@ -530,6 +536,46 @@
return loadDrawable(context);
}
+ /**
+ * Load a drawable, but in the case of URI types, it will check if the passed uid has a grant
+ * to load the resource. The check will be performed using the permissions of the passed uid,
+ * and not those of the caller.
+ * <p>
+ * This should be called for {@link Icon} objects that come from a not trusted source and may
+ * contain a URI.
+ *
+ * After the check, if passed, {@link #loadDrawable} will be called. If failed, this will
+ * return {@code null}.
+ *
+ * @see #loadDrawable
+ *
+ * @hide
+ */
+ @Nullable
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ public Drawable loadDrawableCheckingUriGrant(
+ Context context,
+ IUriGrantsManager iugm,
+ int callingUid,
+ String packageName
+ ) {
+ if (getType() == TYPE_URI || getType() == TYPE_URI_ADAPTIVE_BITMAP) {
+ try {
+ iugm.checkGrantUriPermission_ignoreNonSystem(
+ callingUid,
+ packageName,
+ ContentProvider.getUriWithoutUserId(getUri()),
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ ContentProvider.getUserIdFromUri(getUri())
+ );
+ } catch (SecurityException | RemoteException e) {
+ Log.e(TAG, "Failed to get URI permission for: " + getUri(), e);
+ return null;
+ }
+ }
+ return loadDrawable(context);
+ }
+
/** @hide */
public static final int MIN_ASHMEM_ICON_SIZE = 128 * (1 << 10);
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 7faf380..705e387 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -435,10 +435,10 @@
<dimen name="desktop_mode_handle_menu_windowing_pill_height">52dp</dimen>
<!-- The height of the handle menu's "More Actions" pill in desktop mode, but not freeform. -->
- <dimen name="desktop_mode_handle_menu_more_actions_pill_height">156dp</dimen>
+ <dimen name="desktop_mode_handle_menu_more_actions_pill_height">104dp</dimen>
<!-- The height of the handle menu's "More Actions" pill in freeform desktop windowing mode. -->
- <dimen name="desktop_mode_handle_menu_more_actions_pill_freeform_height">104dp</dimen>
+ <dimen name="desktop_mode_handle_menu_more_actions_pill_freeform_height">52dp</dimen>
<!-- The top margin of the handle menu in desktop mode. -->
<dimen name="desktop_mode_handle_menu_margin_top">4dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
index cc37bd3a4..d45e126 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
@@ -112,7 +112,7 @@
viewPositionOnTouchDown.set(v.translationX, v.translationY)
performedLongClick = false
- v.handler.postDelayed({
+ v.handler?.postDelayed({
if (v.isLongClickable) {
performedLongClick = v.performLongClick()
}
@@ -122,7 +122,7 @@
MotionEvent.ACTION_MOVE -> {
if (!movedEnough && hypot(dx, dy) > touchSlop && !performedLongClick) {
movedEnough = true
- v.handler.removeCallbacksAndMessages(null)
+ v.handler?.removeCallbacksAndMessages(null)
}
if (movedEnough) {
@@ -138,7 +138,7 @@
} else if (!performedLongClick) {
v.performClick()
} else {
- v.handler.removeCallbacksAndMessages(null)
+ v.handler?.removeCallbacksAndMessages(null)
}
velocityTracker.clear()
@@ -146,7 +146,7 @@
}
MotionEvent.ACTION_CANCEL -> {
- v.handler.removeCallbacksAndMessages(null)
+ v.handler?.removeCallbacksAndMessages(null)
velocityTracker.clear()
movedEnough = false
}
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 3d825f0..4ea14f4 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
@@ -720,6 +720,8 @@
mSplitLayout.setDivideRatio(snapPosition);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
+ wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
+ false /* reparentLeafTaskIfRelaunch */);
setRootForceTranslucent(false, wct);
// Make sure the launch options will put tasks in the corresponding split roots
@@ -767,6 +769,8 @@
mSplitLayout.setDivideRatio(snapPosition);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
+ wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
+ false /* reparentLeafTaskIfRelaunch */);
setRootForceTranslucent(false, wct);
options1 = options1 != null ? options1 : new Bundle();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
index 6ea6516..72fc8686 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
@@ -30,7 +30,7 @@
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_ICON;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS;
import android.window.StartingWindowInfo;
@@ -52,7 +52,8 @@
final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0;
final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0;
final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0;
- final boolean allowIcon = (parameter & TYPE_PARAMETER_ALLOW_ICON) != 0;
+ final boolean isSolidColorSplashScreen =
+ (parameter & TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN) != 0;
final boolean legacySplashScreen =
((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0);
final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0;
@@ -66,13 +67,13 @@
+ "processRunning=%b, "
+ "allowTaskSnapshot=%b, "
+ "activityCreated=%b, "
- + "allowIcon=%b, "
+ + "isSolidColorSplashScreen=%b, "
+ "legacySplashScreen=%b, "
+ "activityDrawn=%b, "
+ "windowless=%b, "
+ "topIsHome=%b",
newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated,
- allowIcon, legacySplashScreen, activityDrawn, windowlessSurface,
+ isSolidColorSplashScreen, legacySplashScreen, activityDrawn, windowlessSurface,
topIsHome);
if (windowlessSurface) {
@@ -80,7 +81,7 @@
}
if (!topIsHome) {
if (!processRunning || newTask || (taskSwitch && !activityCreated)) {
- return getSplashscreenType(allowIcon, legacySplashScreen);
+ return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
}
}
@@ -94,18 +95,18 @@
}
}
if (!activityDrawn && !topIsHome) {
- return getSplashscreenType(allowIcon, legacySplashScreen);
+ return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
}
}
return STARTING_WINDOW_TYPE_NONE;
}
- private static int getSplashscreenType(boolean allowIcon, boolean legacySplashScreen) {
- if (allowIcon) {
- return legacySplashScreen ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
+ private static int getSplashscreenType(boolean solidColorSplashScreen,
+ boolean legacySplashScreen) {
+ return solidColorSplashScreen
+ ? STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN
+ : legacySplashScreen
+ ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
: STARTING_WINDOW_TYPE_SPLASH_SCREEN;
- } else {
- return STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
- }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index f82b212..a7a11de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -163,6 +163,9 @@
final ImageButton splitscreenBtn = windowingPillView.findViewById(
R.id.split_screen_button);
final ImageButton floatingBtn = windowingPillView.findViewById(R.id.floating_button);
+ // TODO: Remove once implemented.
+ floatingBtn.setVisibility(View.GONE);
+
final ImageButton desktopBtn = windowingPillView.findViewById(R.id.desktop_button);
fullscreenBtn.setOnClickListener(mOnClickListener);
splitscreenBtn.setOnClickListener(mOnClickListener);
@@ -196,6 +199,9 @@
}
final Button selectBtn = moreActionsPillView.findViewById(R.id.select_button);
selectBtn.setOnClickListener(mOnClickListener);
+ final Button screenshotBtn = moreActionsPillView.findViewById(R.id.screenshot_button);
+ // TODO: Remove once implemented.
+ screenshotBtn.setVisibility(View.GONE);
}
/**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
new file mode 100644
index 0000000..2cd08a4a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip.apps
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.NetflixAppHelper
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import org.junit.Assume
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test entering pip from Netflix app by interacting with the app UI
+ *
+ * To run this test: `atest WMShellFlickerTests:NetflixEnterPipTest`
+ *
+ * Actions:
+ * ```
+ * Launch Netflix and start playing a video
+ * Go home to enter PiP
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited from [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class NetflixEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
+ override val standardAppHelper: NetflixAppHelper = NetflixAppHelper(instrumentation)
+
+ override val defaultEnterPip: FlickerBuilder.() -> Unit = {
+ setup {
+ standardAppHelper.launchViaIntent(
+ wmHelper,
+ NetflixAppHelper.getNetflixWatchVideoIntent("70184207"),
+ ComponentNameMatcher(NetflixAppHelper.PACKAGE_NAME,
+ NetflixAppHelper.WATCH_ACTIVITY)
+ )
+ standardAppHelper.waitForVideoPlaying()
+ }
+ }
+
+ override val defaultTeardown: FlickerBuilder.() -> Unit = {
+ teardown {
+ standardAppHelper.exit(wmHelper)
+ }
+ }
+
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ transitions {
+ tapl.goHomeFromImmersiveFullscreenApp()
+ }
+ }
+
+ @Postsubmit
+ @Test
+ override fun pipOverlayLayerAppearThenDisappear() {
+ // Netflix uses source rect hint, so PiP overlay is never present
+ }
+
+ @Postsubmit
+ @Test
+ override fun focusChanges() {
+ // in gestural nav the focus goes to different activity on swipe up with auto enter PiP
+ Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
+ super.focusChanges()
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0),
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
index b444bad..e37d806 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
@@ -19,15 +19,10 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
-import org.junit.Rule
import org.junit.Test
open class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
index e2ab989..2a50912 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
@@ -19,15 +19,10 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
-import org.junit.Rule
import org.junit.Test
open class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
index 22b8102..d5da1a8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
-import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByDividerGesturalNavLandscape :
DismissSplitScreenByDivider(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
index 3fb014f..7fdcb9b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
-import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByDividerGesturalNavPortrait :
DismissSplitScreenByDivider(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index ea1f942..308e954 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
-import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByGoHomeGesturalNavLandscape :
DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index 8f23a79..39e75bd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
-import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByGoHomeGesturalNavPortrait :
DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
index b0f39e5..e18da17 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
@@ -19,15 +19,10 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
-import org.junit.Rule
import org.junit.Test
open class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
index 6ce8746..00d60e7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
@@ -19,15 +19,10 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
-import org.junit.Rule
import org.junit.Test
open class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index 9f74edf..d7efbc8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 1e4055e..4eece3f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
index c3b8132..d96b056 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
index 7756d04..809b690 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
index c72aa5a..bbdf2d7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
index cc88f27..5c29fd8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
index 87b38b1..a7398eb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
index ca347ed..eae88ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
index 6597819..7e8ee04 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenFromOverviewGesturalNavLandscape :
EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
index 6df31fc..9295c33 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenFromOverviewGesturalNavPortrait :
EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
index 02596c5..4b59e9f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
-import org.junit.Rule
import org.junit.Test
open class SwitchAppByDoubleTapDividerGesturalNavLandscape :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
index 9d579f6..5ff36d4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
-import org.junit.Rule
import org.junit.Test
open class SwitchAppByDoubleTapDividerGesturalNavPortrait :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
index da85342..c0cb721 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
-import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
index 1ae2c9e..8c14088 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
-import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
index b1b56257..7b6614b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
-import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromHomeGesturalNavLandscape :
SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
index 08c437e..5df5be9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
-import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromHomeGesturalNavPortrait :
SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
index efbf86d..9d63003 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
-import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromRecentGesturalNavLandscape :
SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
index f7072fa..9fa04b2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
-import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromRecentGesturalNavPortrait :
SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
index d80d112..9386aa2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
-import org.junit.Rule
import org.junit.Test
open class SwitchBetweenSplitPairsGesturalNavLandscape :
SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
index 30ec37a..5ef2167 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
-import org.junit.Rule
import org.junit.Test
open class SwitchBetweenSplitPairsGesturalNavPortrait :
SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
index 1e086d2..9caab9b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -18,18 +18,13 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
@RunWith(BlockJUnit4ClassRunner::class)
open class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
index 932f892..bf484e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -18,18 +18,13 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
@RunWith(BlockJUnit4ClassRunner::class)
open class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index 2d93047..02c9d30 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -35,7 +35,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.TransitionInfoBuilder;
+import com.android.wm.shell.transition.TransitionInfoBuilder;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
index 270dbc4..83d9f65 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
@@ -39,7 +39,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.TransitionInfoBuilder;
+import com.android.wm.shell.transition.TransitionInfoBuilder;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
similarity index 91%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
index 0dc16f4..cb29a21 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell;
+package com.android.wm.shell.bubbles;
import static com.google.common.truth.Truth.assertThat;
@@ -27,10 +27,7 @@
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.bubbles.BubbleController;
-import com.android.wm.shell.bubbles.BubbleOverflow;
-import com.android.wm.shell.bubbles.BubbleStackView;
-import com.android.wm.shell.bubbles.TestableBubblePositioner;
+import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java
index 9655f97..f8eb50b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java
@@ -38,7 +38,7 @@
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.TransitionInfoBuilder;
+import com.android.wm.shell.transition.TransitionInfoBuilder;
import org.junit.Before;
import org.junit.Test;
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 664fbb2..dea1617 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
@@ -43,7 +43,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.TestRemoteTransition
+import com.android.wm.shell.transition.TestRemoteTransition
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.DisplayController
@@ -180,7 +180,8 @@
controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(3)
// Expect order to be from bottom: home, task1, task2
wct.assertReorderAt(index = 0, homeTask)
@@ -198,7 +199,8 @@
controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(3)
// Expect order to be from bottom: home, task1, task2
wct.assertReorderAt(index = 0, homeTask)
@@ -216,7 +218,8 @@
controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(3)
// Expect order to be from bottom: home, task1, task2
wct.assertReorderAt(index = 0, homeTask)
@@ -230,7 +233,8 @@
controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(1)
wct.assertReorderAt(index = 0, homeTask)
}
@@ -246,7 +250,8 @@
controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(2)
// Expect order to be from bottom: home, task
wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 69f664a..499e339 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -37,8 +37,8 @@
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.TransitionInfoBuilder;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.TransitionInfoBuilder;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
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 d542139..ebc284b 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
@@ -62,9 +62,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestRemoteTransition;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.TransitionInfoBuilder;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -75,6 +73,8 @@
import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.transition.DefaultMixedHandler;
+import com.android.wm.shell.transition.TestRemoteTransition;
+import com.android.wm.shell.transition.TransitionInfoBuilder;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index a57a7bf..d598678 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -94,7 +94,6 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.TransitionInfoBuilder;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java
similarity index 97%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java
index 0df42b3..39ab238 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell;
+package com.android.wm.shell.transition;
import android.os.IBinder;
import android.os.RemoteException;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TransitionInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TransitionInfoBuilder.java
similarity index 98%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TransitionInfoBuilder.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TransitionInfoBuilder.java
index a658375..8343858 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TransitionInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TransitionInfoBuilder.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell;
+package com.android.wm.shell.transition;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index e1dd145..ff1eedb 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -143,6 +143,7 @@
"libcrypto",
"libsync",
"libui",
+ "aconfig_text_flags_c_lib",
],
static_libs: [
"libEGL_blobCache",
@@ -712,11 +713,13 @@
],
static_libs: [
+ "libflagtest",
"libgmock",
"libhwui_static",
],
shared_libs: [
"libmemunreachable",
+ "server_configurable_flags",
],
srcs: [
"tests/unit/main.cpp",
@@ -756,6 +759,7 @@
"tests/unit/TestUtilsTests.cpp",
"tests/unit/ThreadBaseTests.cpp",
"tests/unit/TypefaceTests.cpp",
+ "tests/unit/UnderlineTest.cpp",
"tests/unit/VectorDrawableTests.cpp",
"tests/unit/WebViewFunctorManagerTests.cpp",
],
diff --git a/libs/hwui/FeatureFlags.h b/libs/hwui/FeatureFlags.h
new file mode 100644
index 0000000..ffb329d
--- /dev/null
+++ b/libs/hwui/FeatureFlags.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_FEATURE_FLAGS_H
+#define ANDROID_HWUI_FEATURE_FLAGS_H
+
+#ifdef __ANDROID__
+#include <com_android_text_flags.h>
+#endif // __ANDROID__
+
+namespace android {
+
+namespace text_feature {
+
+inline bool fix_double_underline() {
+#ifdef __ANDROID__
+ return com_android_text_flags_fix_double_underline();
+#else
+ return true;
+#endif // __ANDROID__
+}
+
+} // namespace text_feature
+
+} // namespace android
+
+#endif // ANDROID_HWUI_FEATURE_FLAGS_H
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 8394c3c..31fc929 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -47,6 +47,7 @@
#include <utility>
#include "CanvasProperty.h"
+#include "FeatureFlags.h"
#include "Mesh.h"
#include "NinePatchUtils.h"
#include "VectorDrawable.h"
@@ -795,7 +796,9 @@
sk_sp<SkTextBlob> textBlob(builder.make());
applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); });
- drawTextDecorations(x, y, totalAdvance, paintCopy);
+ if (!text_feature::fix_double_underline()) {
+ drawTextDecorations(x, y, totalAdvance, paintCopy);
+ }
}
void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 2351797..80b6c03 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -16,17 +16,18 @@
#include "Canvas.h"
+#include <SkFontMetrics.h>
+#include <SkRRect.h>
+
+#include "FeatureFlags.h"
#include "MinikinUtils.h"
#include "Paint.h"
#include "Properties.h"
#include "RenderNode.h"
#include "Typeface.h"
-#include "pipeline/skia/SkiaRecordingCanvas.h"
-
+#include "hwui/DrawTextFunctor.h"
#include "hwui/PaintFilter.h"
-
-#include <SkFontMetrics.h>
-#include <SkRRect.h>
+#include "pipeline/skia/SkiaRecordingCanvas.h"
namespace android {
@@ -34,13 +35,6 @@
return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
}
-static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
- const Paint& paint, Canvas* canvas) {
- const SkScalar strokeWidth = fmax(thickness, 1.0f);
- const SkScalar bottom = top + strokeWidth;
- canvas->drawRect(left, top, right, bottom, paint);
-}
-
void Canvas::drawTextDecorations(float x, float y, float length, const Paint& paint) {
// paint has already been filtered by our caller, so we can ignore any filter
const bool strikeThru = paint.isStrikeThru();
@@ -72,73 +66,6 @@
}
}
-static void simplifyPaint(int color, Paint* paint) {
- paint->setColor(color);
- paint->setShader(nullptr);
- paint->setColorFilter(nullptr);
- paint->setLooper(nullptr);
- paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize());
- paint->setStrokeJoin(SkPaint::kRound_Join);
- paint->setLooper(nullptr);
-}
-
-class DrawTextFunctor {
-public:
- DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x,
- float y, float totalAdvance)
- : layout(layout)
- , canvas(canvas)
- , paint(paint)
- , x(x)
- , y(y)
- , totalAdvance(totalAdvance) {}
-
- void operator()(size_t start, size_t end) {
- auto glyphFunc = [&](uint16_t* text, float* positions) {
- for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
- text[textIndex++] = layout.getGlyphId(i);
- positions[posIndex++] = x + layout.getX(i);
- positions[posIndex++] = y + layout.getY(i);
- }
- };
-
- size_t glyphCount = end - start;
-
- if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) {
- // high contrast draw path
- int color = paint.getColor();
- int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
- bool darken = channelSum < (128 * 3);
-
- // outline
- gDrawTextBlobMode = DrawTextBlobMode::HctOutline;
- Paint outlinePaint(paint);
- simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
- outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
- canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
-
- // inner
- gDrawTextBlobMode = DrawTextBlobMode::HctInner;
- Paint innerPaint(paint);
- simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
- innerPaint.setStyle(SkPaint::kFill_Style);
- canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance);
- gDrawTextBlobMode = DrawTextBlobMode::Normal;
- } else {
- // standard draw path
- canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance);
- }
- }
-
-private:
- const minikin::Layout& layout;
- Canvas* canvas;
- const Paint& paint;
- float x;
- float y;
- float totalAdvance;
-};
-
void Canvas::drawGlyphs(const minikin::Font& font, const int* glyphIds, const float* positions,
int glyphCount, const Paint& paint) {
// Minikin modify skFont for auto-fakebold/auto-fakeitalic.
@@ -182,6 +109,31 @@
DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance());
MinikinUtils::forFontRun(layout, &paint, f);
+
+ if (text_feature::fix_double_underline()) {
+ Paint copied(paint);
+ PaintFilter* filter = getPaintFilter();
+ if (filter != nullptr) {
+ filter->filterFullPaint(&copied);
+ }
+ const bool isUnderline = copied.isUnderline();
+ const bool isStrikeThru = copied.isStrikeThru();
+ if (isUnderline || isStrikeThru) {
+ const SkScalar left = x;
+ const SkScalar right = x + layout.getAdvance();
+ if (isUnderline) {
+ const SkScalar top = y + f.getUnderlinePosition();
+ drawStroke(left, right, top, f.getUnderlineThickness(), copied, this);
+ }
+ if (isStrikeThru) {
+ float textSize = paint.getSkFont().getSize();
+ const float position = textSize * Paint::kStdStrikeThru_Top;
+ const SkScalar thickness = textSize * Paint::kStdStrikeThru_Thickness;
+ const SkScalar top = y + position;
+ drawStroke(left, right, top, thickness, copied, this);
+ }
+ }
+ }
}
void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight,
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 44ee31d..9ec023b 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -285,7 +285,7 @@
* totalAdvance: used to define width of text decorations (underlines, strikethroughs).
*/
virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
- float y,float totalAdvance) = 0;
+ float y, float totalAdvance) = 0;
virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
const Paint& paint, const SkPath& path, size_t start,
size_t end) = 0;
diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h
new file mode 100644
index 0000000..2e6e976
--- /dev/null
+++ b/libs/hwui/hwui/DrawTextFunctor.h
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+#include <SkFontMetrics.h>
+#include <SkRRect.h>
+
+#include "Canvas.h"
+#include "FeatureFlags.h"
+#include "MinikinUtils.h"
+#include "Paint.h"
+#include "Properties.h"
+#include "RenderNode.h"
+#include "Typeface.h"
+#include "hwui/PaintFilter.h"
+#include "pipeline/skia/SkiaRecordingCanvas.h"
+
+namespace android {
+
+static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
+ const Paint& paint, Canvas* canvas) {
+ const SkScalar strokeWidth = fmax(thickness, 1.0f);
+ const SkScalar bottom = top + strokeWidth;
+ canvas->drawRect(left, top, right, bottom, paint);
+}
+
+static void simplifyPaint(int color, Paint* paint) {
+ paint->setColor(color);
+ paint->setShader(nullptr);
+ paint->setColorFilter(nullptr);
+ paint->setLooper(nullptr);
+ paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize());
+ paint->setStrokeJoin(SkPaint::kRound_Join);
+ paint->setLooper(nullptr);
+}
+
+class DrawTextFunctor {
+public:
+ DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x,
+ float y, float totalAdvance)
+ : layout(layout)
+ , canvas(canvas)
+ , paint(paint)
+ , x(x)
+ , y(y)
+ , totalAdvance(totalAdvance)
+ , underlinePosition(0)
+ , underlineThickness(0) {}
+
+ void operator()(size_t start, size_t end) {
+ auto glyphFunc = [&](uint16_t* text, float* positions) {
+ for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
+ text[textIndex++] = layout.getGlyphId(i);
+ positions[posIndex++] = x + layout.getX(i);
+ positions[posIndex++] = y + layout.getY(i);
+ }
+ };
+
+ size_t glyphCount = end - start;
+
+ if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) {
+ // high contrast draw path
+ int color = paint.getColor();
+ int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
+ bool darken = channelSum < (128 * 3);
+
+ // outline
+ gDrawTextBlobMode = DrawTextBlobMode::HctOutline;
+ Paint outlinePaint(paint);
+ simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
+ outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
+ canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
+
+ // inner
+ gDrawTextBlobMode = DrawTextBlobMode::HctInner;
+ Paint innerPaint(paint);
+ simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
+ innerPaint.setStyle(SkPaint::kFill_Style);
+ canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance);
+ gDrawTextBlobMode = DrawTextBlobMode::Normal;
+ } else {
+ // standard draw path
+ canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance);
+ }
+
+ if (text_feature::fix_double_underline()) {
+ // Extract underline position and thickness.
+ if (paint.isUnderline()) {
+ SkFontMetrics metrics;
+ paint.getSkFont().getMetrics(&metrics);
+ const float textSize = paint.getSkFont().getSize();
+ SkScalar position;
+ if (!metrics.hasUnderlinePosition(&position)) {
+ position = textSize * Paint::kStdUnderline_Top;
+ }
+ SkScalar thickness;
+ if (!metrics.hasUnderlineThickness(&thickness)) {
+ thickness = textSize * Paint::kStdUnderline_Thickness;
+ }
+
+ // If multiple fonts are used, use the most bottom position and most thick stroke
+ // width as the underline position. This follows the CSS standard:
+ // https://www.w3.org/TR/css-text-decor-3/#text-underline-position-property
+ // <quote>
+ // The exact position and thickness of line decorations is UA-defined in this level.
+ // However, for underlines and overlines the UA must use a single thickness and
+ // position on each line for the decorations deriving from a single decorating box.
+ // </quote>
+ underlinePosition = std::max(underlinePosition, position);
+ underlineThickness = std::max(underlineThickness, thickness);
+ }
+ }
+ }
+
+ float getUnderlinePosition() const { return underlinePosition; }
+ float getUnderlineThickness() const { return underlineThickness; }
+
+private:
+ const minikin::Layout& layout;
+ Canvas* canvas;
+ const Paint& paint;
+ float x;
+ float y;
+ float totalAdvance;
+ float underlinePosition;
+ float underlineThickness;
+};
+
+} // namespace android
diff --git a/libs/hwui/jni/Gainmap.cpp b/libs/hwui/jni/Gainmap.cpp
index cec0ee7..0fffee7 100644
--- a/libs/hwui/jni/Gainmap.cpp
+++ b/libs/hwui/jni/Gainmap.cpp
@@ -208,8 +208,6 @@
p.writeFloat(info.fDisplayRatioHdr);
// base image type
p.writeInt32(static_cast<int32_t>(info.fBaseImageType));
- // type
- p.writeInt32(static_cast<int32_t>(info.fType));
#else
doThrowRE(env, "Cannot use parcels outside of Android!");
#endif
@@ -232,7 +230,6 @@
info.fDisplayRatioSdr = p.readFloat();
info.fDisplayRatioHdr = p.readFloat();
info.fBaseImageType = static_cast<SkGainmapInfo::BaseImageType>(p.readInt32());
- info.fType = static_cast<SkGainmapInfo::Type>(p.readInt32());
fromJava(nativeObject)->info = info;
#else
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 94ed06c..f76ea06 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -17,6 +17,7 @@
#include "RenderThread.h"
#include <GrContextOptions.h>
+#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
#include <android-base/properties.h>
#include <dlfcn.h>
#include <gl/GrGLInterface.h>
@@ -286,7 +287,7 @@
auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
auto size = glesVersion ? strlen(glesVersion) : -1;
cacheManager().configureContext(&options, glesVersion, size);
- sk_sp<GrDirectContext> grContext(GrDirectContext::MakeGL(std::move(glInterface), options));
+ sk_sp<GrDirectContext> grContext(GrDirectContexts::MakeGL(std::move(glInterface), options));
LOG_ALWAYS_FATAL_IF(!grContext.get());
setGrContext(grContext);
}
diff --git a/libs/hwui/tests/unit/UnderlineTest.cpp b/libs/hwui/tests/unit/UnderlineTest.cpp
new file mode 100644
index 0000000..db2be20
--- /dev/null
+++ b/libs/hwui/tests/unit/UnderlineTest.cpp
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+#include <fcntl.h>
+#include <flag_macros.h>
+#include <gtest/gtest.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <utils/Log.h>
+
+#include "SkAlphaType.h"
+#include "SkBitmap.h"
+#include "SkData.h"
+#include "SkFontMgr.h"
+#include "SkImageInfo.h"
+#include "SkRefCnt.h"
+#include "SkStream.h"
+#include "SkTypeface.h"
+#include "SkiaCanvas.h"
+#include "hwui/Bitmap.h"
+#include "hwui/DrawTextFunctor.h"
+#include "hwui/MinikinSkia.h"
+#include "hwui/MinikinUtils.h"
+#include "hwui/Paint.h"
+#include "hwui/Typeface.h"
+
+using namespace android;
+
+namespace {
+
+constexpr char kRobotoVariable[] = "/system/fonts/Roboto-Regular.ttf";
+constexpr char kJPFont[] = "/system/fonts/NotoSansCJK-Regular.ttc";
+
+// The underline position and thickness are cames from post table.
+constexpr float ROBOTO_POSITION_EM = 150.0 / 2048.0;
+constexpr float ROBOTO_THICKNESS_EM = 100.0 / 2048.0;
+constexpr float NOTO_CJK_POSITION_EM = 125.0 / 1000.0;
+constexpr float NOTO_CJK_THICKNESS_EM = 50.0 / 1000.0;
+
+void unmap(const void* ptr, void* context) {
+ void* p = const_cast<void*>(ptr);
+ size_t len = reinterpret_cast<size_t>(context);
+ munmap(p, len);
+}
+
+// Create a font family from a single font file.
+std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) {
+ int fd = open(fileName, O_RDONLY);
+ LOG_ALWAYS_FATAL_IF(fd == -1, "Failed to open file %s", fileName);
+ struct stat st = {};
+ LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", fileName);
+ void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ sk_sp<SkData> skData =
+ SkData::MakeWithProc(data, st.st_size, unmap, reinterpret_cast<void*>(st.st_size));
+ std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(skData));
+ sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+ sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData)));
+ LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
+ std::shared_ptr<minikin::MinikinFont> font =
+ std::make_shared<MinikinFontSkia>(std::move(typeface), 0, data, st.st_size, fileName, 0,
+ std::vector<minikin::FontVariation>());
+ std::vector<std::shared_ptr<minikin::Font>> fonts;
+ fonts.push_back(minikin::Font::Builder(font).build());
+ return minikin::FontFamily::create(std::move(fonts));
+}
+
+// Create a typeface from roboto and NotoCJK.
+Typeface* makeTypeface() {
+ return Typeface::createFromFamilies(
+ std::vector<std::shared_ptr<minikin::FontFamily>>(
+ {buildFamily(kRobotoVariable), buildFamily(kJPFont)}),
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE, nullptr /* fallback */);
+}
+
+// Execute a text layout.
+minikin::Layout doLayout(const std::vector<uint16_t> text, Paint paint, Typeface* typeface) {
+ return MinikinUtils::doLayout(&paint, minikin::Bidi::LTR, typeface, text.data(), text.size(),
+ 0 /* start */, text.size(), 0, text.size(), nullptr);
+}
+
+DrawTextFunctor processFunctor(const std::vector<uint16_t>& text, Paint* paint) {
+ // Create canvas
+ SkImageInfo info = SkImageInfo::Make(1, 1, kN32_SkColorType, kOpaque_SkAlphaType);
+ sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ SkiaCanvas canvas(skBitmap);
+
+ // Create minikin::Layout
+ std::unique_ptr<Typeface> typeface(makeTypeface());
+ minikin::Layout layout = doLayout(text, *paint, typeface.get());
+
+ DrawTextFunctor f(layout, &canvas, *paint, 0, 0, layout.getAdvance());
+ MinikinUtils::forFontRun(layout, paint, f);
+ return f;
+}
+
+TEST_WITH_FLAGS(UnderlineTest, Roboto,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags,
+ fix_double_underline))) {
+ float textSize = 100;
+ Paint paint;
+ paint.getSkFont().setSize(textSize);
+ paint.setUnderline(true);
+ // the text is "abc"
+ DrawTextFunctor functor = processFunctor({0x0061, 0x0062, 0x0063}, &paint);
+
+ EXPECT_EQ(ROBOTO_POSITION_EM * textSize, functor.getUnderlinePosition());
+ EXPECT_EQ(ROBOTO_THICKNESS_EM * textSize, functor.getUnderlineThickness());
+}
+
+TEST_WITH_FLAGS(UnderlineTest, NotoCJK,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags,
+ fix_double_underline))) {
+ float textSize = 100;
+ Paint paint;
+ paint.getSkFont().setSize(textSize);
+ paint.setUnderline(true);
+ // The text is あいう in Japanese
+ DrawTextFunctor functor = processFunctor({0x3042, 0x3044, 0x3046}, &paint);
+
+ EXPECT_EQ(NOTO_CJK_POSITION_EM * textSize, functor.getUnderlinePosition());
+ EXPECT_EQ(NOTO_CJK_THICKNESS_EM * textSize, functor.getUnderlineThickness());
+}
+
+TEST_WITH_FLAGS(UnderlineTest, Mixture,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags,
+ fix_double_underline))) {
+ float textSize = 100;
+ Paint paint;
+ paint.getSkFont().setSize(textSize);
+ paint.setUnderline(true);
+ // The text is aいc. The only middle of the character is Japanese.
+ DrawTextFunctor functor = processFunctor({0x0061, 0x3044, 0x0063}, &paint);
+
+ // We use the bottom, thicker line as underline. Here, use Noto's one.
+ EXPECT_EQ(NOTO_CJK_POSITION_EM * textSize, functor.getUnderlinePosition());
+ EXPECT_EQ(NOTO_CJK_THICKNESS_EM * textSize, functor.getUnderlineThickness());
+}
+} // namespace
diff --git a/location/Android.bp b/location/Android.bp
index cfe0e49..eb7cd01 100644
--- a/location/Android.bp
+++ b/location/Android.bp
@@ -39,3 +39,8 @@
],
},
}
+
+platform_compat_config {
+ name: "framework-location-compat-config",
+ src: ":framework-location",
+}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index b0cdb05..230fb07 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -1167,7 +1167,10 @@
streamType);
if (attributes != null) {
mUsage = attributes.mUsage;
- mContentType = attributes.mContentType;
+ // on purpose ignoring the content type: stream types are deprecated for
+ // playback, making assumptions about the content type is prone to
+ // interpretation errors for ambiguous types such as STREAM_TTS and STREAM_MUSIC
+ //mContentType = attributes.mContentType;
mFlags = attributes.getAllFlags();
mMuteHapticChannels = attributes.areHapticChannelsMuted();
mIsContentSpatialized = attributes.isContentSpatialized();
@@ -1177,49 +1180,47 @@
mSource = attributes.mSource;
}
}
- if (mContentType == CONTENT_TYPE_UNKNOWN) {
- switch (streamType) {
- case AudioSystem.STREAM_VOICE_CALL:
- mContentType = CONTENT_TYPE_SPEECH;
- break;
- case AudioSystem.STREAM_SYSTEM_ENFORCED:
- mFlags |= FLAG_AUDIBILITY_ENFORCED;
- // intended fall through, attributes in common with STREAM_SYSTEM
- case AudioSystem.STREAM_SYSTEM:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_RING:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_MUSIC:
- mContentType = CONTENT_TYPE_MUSIC;
- break;
- case AudioSystem.STREAM_ALARM:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_NOTIFICATION:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_BLUETOOTH_SCO:
- mContentType = CONTENT_TYPE_SPEECH;
- mFlags |= FLAG_SCO;
- break;
- case AudioSystem.STREAM_DTMF:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_TTS:
- mContentType = CONTENT_TYPE_SONIFICATION;
- mFlags |= FLAG_BEACON;
- break;
- case AudioSystem.STREAM_ACCESSIBILITY:
- mContentType = CONTENT_TYPE_SPEECH;
- break;
- case AudioSystem.STREAM_ASSISTANT:
- mContentType = CONTENT_TYPE_SPEECH;
- break;
- default:
- Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes");
- }
+ switch (streamType) {
+ case AudioSystem.STREAM_VOICE_CALL:
+ mContentType = CONTENT_TYPE_SPEECH;
+ break;
+ case AudioSystem.STREAM_SYSTEM_ENFORCED:
+ mFlags |= FLAG_AUDIBILITY_ENFORCED;
+ // intended fall through, attributes in common with STREAM_SYSTEM
+ case AudioSystem.STREAM_SYSTEM:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_RING:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_ALARM:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_NOTIFICATION:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_BLUETOOTH_SCO:
+ mContentType = CONTENT_TYPE_SPEECH;
+ mFlags |= FLAG_SCO;
+ break;
+ case AudioSystem.STREAM_DTMF:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_TTS:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mFlags |= FLAG_BEACON;
+ break;
+ case AudioSystem.STREAM_ACCESSIBILITY:
+ mContentType = CONTENT_TYPE_SPEECH;
+ break;
+ case AudioSystem.STREAM_ASSISTANT:
+ mContentType = CONTENT_TYPE_SPEECH;
+ break;
+ case AudioSystem.STREAM_MUSIC:
+ // leaving as CONTENT_TYPE_UNKNOWN
+ break;
+ default:
+ Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes");
}
if (mUsage == USAGE_UNKNOWN) {
mUsage = usageForStreamType(streamType);
diff --git a/media/java/android/media/projection/TEST_MAPPING b/media/java/android/media/projection/TEST_MAPPING
index a792498..7aa9118 100644
--- a/media/java/android/media/projection/TEST_MAPPING
+++ b/media/java/android/media/projection/TEST_MAPPING
@@ -4,9 +4,6 @@
"name": "MediaProjectionTests",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index a4e5fb6..196b5c3 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -401,7 +401,9 @@
private void resetInternal() {
mSessionCallback = null;
- mPendingAppPrivateCommands.clear();
+ synchronized (mPendingAppPrivateCommands) {
+ mPendingAppPrivateCommands.clear();
+ }
if (mSession != null) {
setSessionSurface(null);
removeSessionOverlayView();
@@ -691,7 +693,10 @@
} else {
Log.w(TAG, "sendAppPrivateCommand - session not yet created (action \"" + action
+ "\" pending)");
- mPendingAppPrivateCommands.add(Pair.create(action, data));
+
+ synchronized (mPendingAppPrivateCommands) {
+ mPendingAppPrivateCommands.add(Pair.create(action, data));
+ }
}
}
@@ -1320,10 +1325,13 @@
mSession = session;
if (session != null) {
// Sends the pending app private commands first.
- for (Pair<String, Bundle> command : mPendingAppPrivateCommands) {
- mSession.sendAppPrivateCommand(command.first, command.second);
+
+ synchronized (mPendingAppPrivateCommands) {
+ for (Pair<String, Bundle> command : mPendingAppPrivateCommands) {
+ mSession.sendAppPrivateCommand(command.first, command.second);
+ }
+ mPendingAppPrivateCommands.clear();
}
- mPendingAppPrivateCommands.clear();
synchronized (sMainTvViewLock) {
if (hasWindowFocus() && TvView.this == sMainTvView.get()
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f9ea773..49bd9d9 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -345,6 +345,8 @@
<string name="accessibility_wifi_three_bars">Wifi three bars.</string>
<!-- Content description of the WIFI signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_wifi_signal_full">Wifi signal full.</string>
+ <!-- Content description of the WIFI signal when the WIFI is connected using the signal from a different device owned by the user. For accessibility (not shown on the screen) [CHAR LIMIT=NONE] -->
+ <string name="accessibility_wifi_other_device">Connected to your device.</string>
<!-- Content description of the Wi-Fi security type. This message indicates this is an open Wi-Fi (no password needed) [CHAR LIMIT=NONE] -->
<string name="accessibility_wifi_security_type_none">Open network</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
index ee65ef4..ce466df 100644
--- a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
+++ b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
@@ -50,6 +50,7 @@
};
public static final int WIFI_NO_CONNECTION = R.string.accessibility_no_wifi;
+ public static final int WIFI_OTHER_DEVICE_CONNECTION = R.string.accessibility_wifi_other_device;
public static final int NO_CALLING = R.string.accessibility_no_calling;
diff --git a/packages/SettingsLib/tests/integ/AndroidManifest.xml b/packages/SettingsLib/tests/integ/AndroidManifest.xml
index a95da303..32048ca 100644
--- a/packages/SettingsLib/tests/integ/AndroidManifest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidManifest.xml
@@ -41,7 +41,7 @@
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.settingslib"
+ android:targetPackage="com.android.settingslib.tests.integ"
android:label="Tests for SettingsLib">
</instrumentation>
</manifest>
diff --git a/packages/SettingsLib/tests/integ/AndroidTest.xml b/packages/SettingsLib/tests/integ/AndroidTest.xml
index b5d0947..d0aee88 100644
--- a/packages/SettingsLib/tests/integ/AndroidTest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidTest.xml
@@ -21,7 +21,7 @@
<option name="test-suite-tag" value="apct" />
<option name="test-tag" value="SettingsLibTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="com.android.settingslib" />
+ <option name="package" value="com.android.settingslib.tests.integ" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 1c33544..f6f75de 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -143,6 +143,7 @@
Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
Settings.Secure.SCREENSAVER_HOME_CONTROLS_ENABLED,
Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
+ Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT,
Settings.Secure.VOLUME_HUSH_GESTURE,
Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT,
Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
@@ -243,6 +244,7 @@
Settings.Secure.HEARING_AID_SYSTEM_SOUNDS_ROUTING,
Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED,
- Settings.Secure.SEARCH_LONG_PRESS_HOME_ENABLED
+ Settings.Secure.SEARCH_LONG_PRESS_HOME_ENABLED,
+ Settings.Secure.HUB_MODE_TUTORIAL_STATE
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 301fd6f..8d13f01 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -212,6 +212,7 @@
VALIDATORS.put(Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SCREENSAVER_HOME_CONTROLS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.VOLUME_HUSH_GESTURE, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(
Secure.ENABLED_NOTIFICATION_LISTENERS,
@@ -389,5 +390,6 @@
VALIDATORS.put(Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_EDGE_HAPTIC_ENABLED,
BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DND_CONFIGS_MIGRATED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.HUB_MODE_TUTORIAL_STATE, NON_NEGATIVE_INTEGER_VALIDATOR);
}
}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 104f3d2..ee05f2d 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -363,6 +363,8 @@
"tests/src/com/android/systemui/qs/pipeline/data/**/*Test.kt",
"tests/src/com/android/systemui/qs/pipeline/domain/**/*Test.kt",
"tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt",
+ "tests/src/com/android/systemui/qs/tiles/base/**/*.kt",
+ "tests/src/com/android/systemui/qs/tiles/viewmodel/**/*.kt",
],
path: "tests/src",
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b5b873c..9bfc4be 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -634,7 +634,7 @@
<!-- started from MediaProjectionManager -->
<activity
- android:name=".media.MediaProjectionPermissionActivity"
+ android:name=".mediaprojection.permission.MediaProjectionPermissionActivity"
android:exported="true"
android:theme="@style/Theme.SystemUI.MediaProjectionAlertDialog"
android:finishOnCloseSystemDialogs="true"
@@ -643,7 +643,7 @@
android:visibleToInstantApps="true"/>
<activity
- android:name=".media.MediaProjectionAppSelectorActivity"
+ android:name=".mediaprojection.appselector.MediaProjectionAppSelectorActivity"
android:theme="@style/Theme.SystemUI.MediaProjectionAppSelector"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 5c2f979..0623d4a 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -69,7 +69,7 @@
"exclude-annotation": "org.junit.Ignore"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permissionui.cts.CameraMicIndicatorsPermissionTest"
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt
index 0cc259a..a62c984 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt
@@ -151,7 +151,7 @@
element.lastAlpha = alpha
}
}
- .testTag(key.name)
+ .testTag(key.testTag)
}
private fun shouldDrawElement(
@@ -167,7 +167,8 @@
state.fromScene == state.toScene ||
!layoutImpl.isTransitionReady(state) ||
state.fromScene !in element.sceneValues ||
- state.toScene !in element.sceneValues
+ state.toScene !in element.sceneValues ||
+ !isSharedElementEnabled(layoutImpl, state, element.key)
) {
return true
}
@@ -191,6 +192,26 @@
}
}
+private fun isSharedElementEnabled(
+ layoutImpl: SceneTransitionLayoutImpl,
+ transition: TransitionState.Transition,
+ element: ElementKey,
+): Boolean {
+ val spec = layoutImpl.transitions.transitionSpec(transition.fromScene, transition.toScene)
+ val sharedInFromScene = spec.transformations(element, transition.fromScene).shared
+ val sharedInToScene = spec.transformations(element, transition.toScene).shared
+
+ // The sharedElement() transformation must either be null or be the same in both scenes.
+ if (sharedInFromScene != sharedInToScene) {
+ error(
+ "Different sharedElement() transformations matched $element (from=$sharedInFromScene " +
+ "to=$sharedInToScene)"
+ )
+ }
+
+ return sharedInFromScene?.enabled ?: true
+}
+
/**
* Chain the [com.android.compose.animation.scene.transformation.ModifierTransformation] applied
* throughout the current transition, if any.
@@ -213,7 +234,7 @@
return layoutImpl.transitions
.transitionSpec(fromScene, state.toScene)
- .transformations(element.key)
+ .transformations(element.key, scene.key)
.modifier
.fold(this) { modifier, transformation ->
with(transformation) {
@@ -407,17 +428,20 @@
// The element is shared: interpolate between the value in fromScene and the value in toScene.
// TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared
// elements follow the finger direction.
- if (fromValues != null && toValues != null) {
+ val isSharedElement = fromValues != null && toValues != null
+ if (isSharedElement && isSharedElementEnabled(layoutImpl, state, element.key)) {
return lerp(
- sceneValue(fromValues),
- sceneValue(toValues),
+ sceneValue(fromValues!!),
+ sceneValue(toValues!!),
transitionProgress,
)
}
val transformation =
transformation(
- layoutImpl.transitions.transitionSpec(fromScene, toScene).transformations(element.key)
+ layoutImpl.transitions
+ .transitionSpec(fromScene, toScene)
+ .transformations(element.key, scene.key)
)
// If there is no transformation explicitly associated to this element value, let's use
// the value given by the system (like the current position and size given by the layout
@@ -426,12 +450,21 @@
// Get the transformed value, i.e. the target value at the beginning (for entering elements) or
// end (for leaving elements) of the transition.
+ val sceneValues =
+ checkNotNull(
+ when {
+ isSharedElement && scene.key == fromScene -> fromValues
+ isSharedElement -> toValues
+ else -> fromValues ?: toValues
+ }
+ )
+
val targetValue =
transformation.transform(
layoutImpl,
scene,
element,
- fromValues ?: toValues!!,
+ sceneValues,
state,
idleValue,
)
@@ -440,7 +473,7 @@
val rangeProgress = transformation.range?.progress(transitionProgress) ?: transitionProgress
// Interpolate between the value at rest and the value before entering/after leaving.
- val isEntering = fromValues == null
+ val isEntering = scene.key == toScene
return if (isEntering) {
lerp(targetValue, idleValue, rangeProgress)
} else {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ElementMatcher.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ElementMatcher.kt
new file mode 100644
index 0000000..98dbb67
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ElementMatcher.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.compose.animation.scene
+
+/** An interface to match one or more elements. */
+interface ElementMatcher {
+ /** Whether the element with key [key] in scene [scene] matches this matcher. */
+ fun matches(key: ElementKey, scene: SceneKey): Boolean
+}
+
+/**
+ * Returns an [ElementMatcher] that matches elements in [scene] also matching [this]
+ * [ElementMatcher].
+ */
+fun ElementMatcher.inScene(scene: SceneKey): ElementMatcher {
+ val delegate = this
+ val matcherScene = scene
+ return object : ElementMatcher {
+ override fun matches(key: ElementKey, scene: SceneKey): Boolean {
+ return scene == matcherScene && delegate.matches(key, scene)
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt
index f7ebe2f..b7acc48 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt
@@ -16,6 +16,8 @@
package com.android.compose.animation.scene
+import androidx.annotation.VisibleForTesting
+
/**
* A base class to create unique keys, associated to an [identity] that is used to check the
* equality of two key instances.
@@ -41,6 +43,7 @@
name: String,
identity: Any = Object(),
) : Key(name, identity) {
+ @VisibleForTesting val testTag: String = "scene:$name"
/** The unique [ElementKey] identifying this scene's root element. */
val rootElementKey = ElementKey(name, identity)
@@ -61,7 +64,9 @@
*/
val isBackground: Boolean = false,
) : Key(name, identity), ElementMatcher {
- override fun matches(key: ElementKey): Boolean {
+ @VisibleForTesting val testTag: String = "element:$name"
+
+ override fun matches(key: ElementKey, scene: SceneKey): Boolean {
return key == this
}
@@ -73,7 +78,9 @@
/** Matches any element whose [key identity][ElementKey.identity] matches [predicate]. */
fun withIdentity(predicate: (Any) -> Boolean): ElementMatcher {
return object : ElementMatcher {
- override fun matches(key: ElementKey): Boolean = predicate(key.identity)
+ override fun matches(key: ElementKey, scene: SceneKey): Boolean {
+ return predicate(key.identity)
+ }
}
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt
index b44c8ef..3985233 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt
@@ -25,6 +25,7 @@
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.zIndex
@@ -45,7 +46,9 @@
@Composable
fun Content(modifier: Modifier = Modifier) {
- Box(modifier.zIndex(zIndex).onPlaced { size = it.size }) { scope.content() }
+ Box(modifier.zIndex(zIndex).onPlaced { size = it.size }.testTag(key.testTag)) {
+ scope.content()
+ }
}
override fun toString(): String {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 350b9c2..b3a7a8e9 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -158,26 +158,14 @@
BackHandler { onChangeScene(backScene) }
}
- Box(
- Modifier.drawWithContent {
- drawContent()
-
- // At this point, all scenes in scenesToCompose are fully laid out so they
- // are marked as ready. This is necessary because the animation code needs
- // to know the position and size of the elements in each scenes they are in,
- // so [readyScenes] will be used to decide whether the transition is ready
- // (see isTransitionReady() below).
- //
- // We can't do that in a DisposableEffect or SideEffect because those are
- // run between composition and layout. LaunchedEffect could work and might
- // be better, but it looks like launched effects run a frame later than this
- // code so doing this here seems better for performance.
- scenesToCompose.fastForEach { readyScenes[it.key] = true }
- }
- ) {
+ Box {
scenesToCompose.fastForEach { scene ->
val key = scene.key
key(key) {
+ // Mark this scene as ready once it has been composed, laid out and
+ // drawn the first time. We have to do this in a LaunchedEffect here
+ // because DisposableEffect runs between composition and layout.
+ LaunchedEffect(key) { readyScenes[key] = true }
DisposableEffect(key) { onDispose { readyScenes.remove(key) } }
scene.Content(
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt
index f4e3902..75dcb2e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -16,6 +16,7 @@
package com.android.compose.animation.scene
+import androidx.annotation.VisibleForTesting
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.snap
import androidx.compose.ui.geometry.Offset
@@ -28,6 +29,7 @@
import com.android.compose.animation.scene.transformation.PropertyTransformation
import com.android.compose.animation.scene.transformation.RangedPropertyTransformation
import com.android.compose.animation.scene.transformation.ScaleSize
+import com.android.compose.animation.scene.transformation.SharedElementTransformation
import com.android.compose.animation.scene.transformation.Transformation
import com.android.compose.animation.scene.transformation.Translate
import com.android.compose.ui.util.fastForEach
@@ -35,11 +37,12 @@
/** The transitions configuration of a [SceneTransitionLayout]. */
class SceneTransitions(
- private val transitionSpecs: List<TransitionSpec>,
+ @get:VisibleForTesting val transitionSpecs: List<TransitionSpec>,
) {
private val cache = mutableMapOf<SceneKey, MutableMap<SceneKey, TransitionSpec>>()
- internal fun transitionSpec(from: SceneKey, to: SceneKey): TransitionSpec {
+ @VisibleForTesting
+ fun transitionSpec(from: SceneKey, to: SceneKey): TransitionSpec {
return cache.getOrPut(from) { mutableMapOf() }.getOrPut(to) { findSpec(from, to) }
}
@@ -97,7 +100,8 @@
val transformations: List<Transformation>,
val spec: AnimationSpec<Float>,
) {
- private val cache = mutableMapOf<ElementKey, ElementTransformations>()
+ // TODO(b/302300957): Make sure this cache does not infinitely grow.
+ private val cache = mutableMapOf<ElementKey, MutableMap<SceneKey, ElementTransformations>>()
internal fun reverse(): TransitionSpec {
return copy(
@@ -107,12 +111,18 @@
)
}
- internal fun transformations(element: ElementKey): ElementTransformations {
- return cache.getOrPut(element) { computeTransformations(element) }
+ internal fun transformations(element: ElementKey, scene: SceneKey): ElementTransformations {
+ return cache
+ .getOrPut(element) { mutableMapOf() }
+ .getOrPut(scene) { computeTransformations(element, scene) }
}
/** Filter [transformations] to compute the [ElementTransformations] of [element]. */
- private fun computeTransformations(element: ElementKey): ElementTransformations {
+ private fun computeTransformations(
+ element: ElementKey,
+ scene: SceneKey,
+ ): ElementTransformations {
+ var shared: SharedElementTransformation? = null
val modifier = mutableListOf<ModifierTransformation>()
var offset: PropertyTransformation<Offset>? = null
var size: PropertyTransformation<IntSize>? = null
@@ -126,16 +136,16 @@
is Translate,
is EdgeTranslate,
is AnchoredTranslate -> {
- throwIfNotNull(offset, element, property = "offset")
+ throwIfNotNull(offset, element, name = "offset")
offset = root as PropertyTransformation<Offset>
}
is ScaleSize,
is AnchoredSize -> {
- throwIfNotNull(size, element, property = "size")
+ throwIfNotNull(size, element, name = "size")
size = root as PropertyTransformation<IntSize>
}
is Fade -> {
- throwIfNotNull(alpha, element, property = "alpha")
+ throwIfNotNull(alpha, element, name = "alpha")
alpha = root as PropertyTransformation<Float>
}
is RangedPropertyTransformation -> onPropertyTransformation(root, current.delegate)
@@ -143,32 +153,37 @@
}
transformations.fastForEach { transformation ->
- if (!transformation.matcher.matches(element)) {
+ if (!transformation.matcher.matches(element, scene)) {
return@fastForEach
}
when (transformation) {
+ is SharedElementTransformation -> {
+ throwIfNotNull(shared, element, name = "shared")
+ shared = transformation
+ }
is ModifierTransformation -> modifier.add(transformation)
is PropertyTransformation<*> -> onPropertyTransformation(transformation)
}
}
- return ElementTransformations(modifier, offset, size, alpha)
+ return ElementTransformations(shared, modifier, offset, size, alpha)
}
private fun throwIfNotNull(
- previous: PropertyTransformation<*>?,
+ previous: Transformation?,
element: ElementKey,
- property: String,
+ name: String,
) {
if (previous != null) {
- error("$element has multiple transformations for its $property property")
+ error("$element has multiple $name transformations")
}
}
}
/** The transformations of an element during a transition. */
internal class ElementTransformations(
+ val shared: SharedElementTransformation?,
val modifier: List<ModifierTransformation>,
val offset: PropertyTransformation<Offset>?,
val size: PropertyTransformation<IntSize>?,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt
index fb12b90..4966977 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -116,6 +116,14 @@
)
/**
+ * Configure the shared transition when [matcher] is shared between two scenes.
+ *
+ * @param enabled whether the matched element(s) should actually be shared in this transition.
+ * Defaults to true.
+ */
+ fun sharedElement(matcher: ElementMatcher, enabled: Boolean = true)
+
+ /**
* Punch a hole in the element(s) matching [matcher] that has the same bounds as [bounds] and
* using the given [shape].
*
@@ -127,6 +135,13 @@
* the result.
*/
fun punchHole(matcher: ElementMatcher, bounds: ElementKey, shape: Shape = RectangleShape)
+
+ /**
+ * Adds the transformations in [builder] but in reversed order. This allows you to partially
+ * reuse the definition of the transition from scene `Foo` to scene `Bar` inside the definition
+ * of the transition from scene `Bar` to scene `Foo`.
+ */
+ fun reversed(builder: TransitionBuilder.() -> Unit)
}
@TransitionDsl
@@ -179,12 +194,6 @@
fun anchoredSize(matcher: ElementMatcher, anchor: ElementKey)
}
-/** An interface to match one or more elements. */
-interface ElementMatcher {
- /** Whether the element with key [key] matches this matcher. */
- fun matches(key: ElementKey): Boolean
-}
-
/** The edge of a [SceneTransitionLayout]. */
enum class Edge {
Left,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 48d5638e8b..f1c2717 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -31,6 +31,7 @@
import com.android.compose.animation.scene.transformation.PunchHole
import com.android.compose.animation.scene.transformation.RangedPropertyTransformation
import com.android.compose.animation.scene.transformation.ScaleSize
+import com.android.compose.animation.scene.transformation.SharedElementTransformation
import com.android.compose.animation.scene.transformation.Transformation
import com.android.compose.animation.scene.transformation.TransformationRange
import com.android.compose.animation.scene.transformation.Translate
@@ -80,6 +81,7 @@
override var spec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessLow)
private var range: TransformationRange? = null
+ private var reversed = false
private val durationMillis: Int by lazy {
val spec = spec
if (spec !is DurationBasedAnimationSpec) {
@@ -93,6 +95,12 @@
transformations.add(PunchHole(matcher, bounds, shape))
}
+ override fun reversed(builder: TransitionBuilder.() -> Unit) {
+ reversed = true
+ builder()
+ reversed = false
+ }
+
override fun fractionRange(
start: Float?,
end: Float?,
@@ -103,6 +111,10 @@
range = null
}
+ override fun sharedElement(matcher: ElementMatcher, enabled: Boolean) {
+ transformations.add(SharedElementTransformation(matcher, enabled))
+ }
+
override fun timestampRange(
startMillis: Int?,
endMillis: Int?,
@@ -122,11 +134,20 @@
}
private fun transformation(transformation: PropertyTransformation<*>) {
- if (range != null) {
- transformations.add(RangedPropertyTransformation(transformation, range!!))
- } else {
- transformations.add(transformation)
- }
+ val transformation =
+ if (range != null) {
+ RangedPropertyTransformation(transformation, range!!)
+ } else {
+ transformation
+ }
+
+ transformations.add(
+ if (reversed) {
+ transformation.reverse()
+ } else {
+ transformation
+ }
+ )
}
override fun fade(matcher: ElementMatcher) {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt
index ce6749d..a650254 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -30,6 +30,14 @@
*/
val matcher: ElementMatcher
+ /**
+ * The range during which the transformation is applied. If it is `null`, then the
+ * transformation will be applied throughout the whole scene transition.
+ */
+ // TODO(b/240432457): Move this back to PropertyTransformation.
+ val range: TransformationRange?
+ get() = null
+
/*
* Reverse this transformation. This is called when we use Transition(from = A, to = B) when
* animating from B to A and there is no Transition(from = B, to = A) defined.
@@ -37,6 +45,11 @@
fun reverse(): Transformation = this
}
+internal class SharedElementTransformation(
+ override val matcher: ElementMatcher,
+ internal val enabled: Boolean,
+) : Transformation
+
/** A transformation that is applied on the element during the whole transition. */
internal interface ModifierTransformation : Transformation {
/** Apply the transformation to [element]. */
@@ -53,13 +66,6 @@
/** A transformation that changes the value of an element property, like its size or offset. */
internal sealed interface PropertyTransformation<T> : Transformation {
/**
- * The range during which the transformation is applied. If it is `null`, then the
- * transformation will be applied throughout the whole scene transition.
- */
- val range: TransformationRange?
- get() = null
-
- /**
* Transform [value], i.e. the value of the transformed property without this transformation.
*/
// TODO(b/290184746): Figure out a public API for custom transformations that don't have access
@@ -92,8 +98,7 @@
}
/** The progress-based range of a [PropertyTransformation]. */
-data class TransformationRange
-private constructor(
+data class TransformationRange(
val start: Float,
val end: Float,
) {
@@ -133,6 +138,6 @@
}
companion object {
- private const val BoundUnspecified = Float.MIN_VALUE
+ const val BoundUnspecified = Float.MIN_VALUE
}
}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index 8bd6545..328866e 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -224,7 +224,7 @@
// In scene A, the shared element SharedFoo() is at the top end of the layout and has a size
// of 50.dp.
- var sharedFoo = rule.onNodeWithTag(TestElements.Foo.name, useUnmergedTree = true)
+ var sharedFoo = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true)
sharedFoo.assertWidthIsEqualTo(50.dp)
sharedFoo.assertHeightIsEqualTo(50.dp)
sharedFoo.assertPositionInRootIsEqualTo(
@@ -250,7 +250,7 @@
// We need to use onAllNodesWithTag().onFirst() here given that shared elements are
// composed and laid out in both scenes (but drawn only in one).
- sharedFoo = rule.onAllNodesWithTag(TestElements.Foo.name).onFirst()
+ sharedFoo = rule.onAllNodesWithTag(TestElements.Foo.testTag).onFirst()
// In scene B, foo is at the top start (x = 0, y = 0) of the layout and has a size of
// 100.dp. We pause at the middle of the transition, so it should now be 75.dp given that we
@@ -284,7 +284,7 @@
val expectedLeft = 0.dp
val expectedSize = 100.dp + (150.dp - 100.dp) * interpolatedProgress
- sharedFoo = rule.onAllNodesWithTag(TestElements.Foo.name).onFirst()
+ sharedFoo = rule.onAllNodesWithTag(TestElements.Foo.testTag).onFirst()
assertThat((layoutState.transitionState as TransitionState.Transition).progress)
.isEqualTo(interpolatedProgress)
sharedFoo.assertWidthIsEqualTo(expectedSize)
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt
index 275149a0..268057f 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt
@@ -23,7 +23,11 @@
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.SemanticsNodeInteractionCollection
+import androidx.compose.ui.test.hasParent
+import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onNodeWithTag
@DslMarker annotation class TransitionTestDsl
@@ -59,8 +63,21 @@
@TransitionTestDsl
interface TransitionTestAssertionScope {
- /** Assert on [element]. */
- fun onElement(element: ElementKey): SemanticsNodeInteraction
+ /**
+ * Assert on [element].
+ *
+ * Note that presence/value assertions on the returned [SemanticsNodeInteraction] will fail if 0
+ * or more than 1 elements matched [element]. If you need to assert on a shared element that
+ * will be present multiple times in the layout during transitions, either specify the [scene]
+ * in which you are matching or use [onSharedElement] instead.
+ */
+ fun onElement(element: ElementKey, scene: SceneKey? = null): SemanticsNodeInteraction
+
+ /**
+ * Assert on a shared [element]. This will throw if [element] is not shared and present only in
+ * one scene during a transition.
+ */
+ fun onSharedElement(element: ElementKey): SemanticsNodeInteractionCollection
}
/**
@@ -73,20 +90,22 @@
toSceneContent: @Composable SceneScope.() -> Unit,
transition: TransitionBuilder.() -> Unit,
layoutModifier: Modifier = Modifier,
+ fromScene: SceneKey = TestScenes.SceneA,
+ toScene: SceneKey = TestScenes.SceneB,
builder: TransitionTestBuilder.() -> Unit,
) {
testTransition(
- from = TestScenes.SceneA,
- to = TestScenes.SceneB,
+ from = fromScene,
+ to = toScene,
transitionLayout = { currentScene, onChangeScene ->
SceneTransitionLayout(
currentScene,
onChangeScene,
- transitions { from(TestScenes.SceneA, to = TestScenes.SceneB, transition) },
+ transitions { from(fromScene, to = toScene, transition) },
layoutModifier.fillMaxSize(),
) {
- scene(TestScenes.SceneA, content = fromSceneContent)
- scene(TestScenes.SceneB, content = toSceneContent)
+ scene(fromScene, content = fromSceneContent)
+ scene(toScene, content = toSceneContent)
}
},
builder,
@@ -111,8 +130,24 @@
val test = transitionTest(builder)
val assertionScope =
object : TransitionTestAssertionScope {
- override fun onElement(element: ElementKey): SemanticsNodeInteraction {
- return this@testTransition.onNodeWithTag(element.name)
+ override fun onElement(
+ element: ElementKey,
+ scene: SceneKey?
+ ): SemanticsNodeInteraction {
+ return if (scene == null) {
+ onNodeWithTag(element.testTag)
+ } else {
+ onNode(hasTestTag(element.testTag) and hasParent(hasTestTag(scene.testTag)))
+ }
+ }
+
+ override fun onSharedElement(element: ElementKey): SemanticsNodeInteractionCollection {
+ val interaction = onAllNodesWithTag(element.testTag)
+ val matches = interaction.fetchSemanticsNodes(atLeastOneRootRequired = false).size
+ if (matches < 2) {
+ error("Element $element is not shared ($matches matches)")
+ }
+ return interaction
}
}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
new file mode 100644
index 0000000..fa94b250
--- /dev/null
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
@@ -0,0 +1,190 @@
+/*
+ * 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.compose.animation.scene
+
+import androidx.compose.animation.core.SpringSpec
+import androidx.compose.animation.core.TweenSpec
+import androidx.compose.animation.core.tween
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.transformation.Transformation
+import com.android.compose.animation.scene.transformation.TransformationRange
+import com.google.common.truth.Correspondence
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class TransitionDslTest {
+ @Test
+ fun emptyTransitions() {
+ val transitions = transitions {}
+ assertThat(transitions.transitionSpecs).isEmpty()
+ }
+
+ @Test
+ fun manyTransitions() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB)
+ from(TestScenes.SceneB, to = TestScenes.SceneC)
+ from(TestScenes.SceneC, to = TestScenes.SceneA)
+ }
+ assertThat(transitions.transitionSpecs).hasSize(3)
+ }
+
+ @Test
+ fun toFromBuilders() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB)
+ from(TestScenes.SceneB)
+ to(TestScenes.SceneC)
+ }
+
+ assertThat(transitions.transitionSpecs)
+ .comparingElementsUsing(
+ Correspondence.transforming<TransitionSpec, Pair<SceneKey?, SceneKey?>>(
+ { it?.from to it?.to },
+ "has (from, to) equal to"
+ )
+ )
+ .containsExactly(
+ TestScenes.SceneA to TestScenes.SceneB,
+ TestScenes.SceneB to null,
+ null to TestScenes.SceneC,
+ )
+ }
+
+ @Test
+ fun defaultTransitionSpec() {
+ val transitions = transitions { from(TestScenes.SceneA, to = TestScenes.SceneB) }
+ val transition = transitions.transitionSpecs.single()
+ assertThat(transition.spec).isInstanceOf(SpringSpec::class.java)
+ }
+
+ @Test
+ fun customTransitionSpec() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) { spec = tween(durationMillis = 42) }
+ }
+ val transition = transitions.transitionSpecs.single()
+ assertThat(transition.spec).isInstanceOf(TweenSpec::class.java)
+ assertThat((transition.spec as TweenSpec).durationMillis).isEqualTo(42)
+ }
+
+ @Test
+ fun defaultRange() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) { fade(TestElements.Foo) }
+ }
+
+ val transition = transitions.transitionSpecs.single()
+ assertThat(transition.transformations.size).isEqualTo(1)
+ assertThat(transition.transformations.single().range).isEqualTo(null)
+ }
+
+ @Test
+ fun fractionRange() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) {
+ fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
+ fractionRange(start = 0.2f) { fade(TestElements.Foo) }
+ fractionRange(end = 0.9f) { fade(TestElements.Foo) }
+ }
+ }
+
+ val transition = transitions.transitionSpecs.single()
+ assertThat(transition.transformations)
+ .comparingElementsUsing(TRANSFORMATION_RANGE)
+ .containsExactly(
+ TransformationRange(start = 0.1f, end = 0.8f),
+ TransformationRange(start = 0.2f, end = TransformationRange.BoundUnspecified),
+ TransformationRange(start = TransformationRange.BoundUnspecified, end = 0.9f),
+ )
+ }
+
+ @Test
+ fun timestampRange() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) {
+ spec = tween(500)
+
+ timestampRange(startMillis = 100, endMillis = 300) { fade(TestElements.Foo) }
+ timestampRange(startMillis = 200) { fade(TestElements.Foo) }
+ timestampRange(endMillis = 400) { fade(TestElements.Foo) }
+ }
+ }
+
+ val transition = transitions.transitionSpecs.single()
+ assertThat(transition.transformations)
+ .comparingElementsUsing(TRANSFORMATION_RANGE)
+ .containsExactly(
+ TransformationRange(start = 100 / 500f, end = 300 / 500f),
+ TransformationRange(start = 200 / 500f, end = TransformationRange.BoundUnspecified),
+ TransformationRange(start = TransformationRange.BoundUnspecified, end = 400 / 500f),
+ )
+ }
+
+ @Test
+ fun reversed() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) {
+ spec = tween(500)
+ reversed {
+ fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
+ timestampRange(startMillis = 100, endMillis = 300) { fade(TestElements.Foo) }
+ }
+ }
+ }
+
+ val transition = transitions.transitionSpecs.single()
+ assertThat(transition.transformations)
+ .comparingElementsUsing(TRANSFORMATION_RANGE)
+ .containsExactly(
+ TransformationRange(start = 1f - 0.8f, end = 1f - 0.1f),
+ TransformationRange(start = 1f - 300 / 500f, end = 1f - 100 / 500f),
+ )
+ }
+
+ @Test
+ fun defaultReversed() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) {
+ spec = tween(500)
+ fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
+ timestampRange(startMillis = 100, endMillis = 300) { fade(TestElements.Foo) }
+ }
+ }
+
+ // Fetch the transition from B to A, which will automatically reverse the transition from A
+ // to B we defined.
+ val transition =
+ transitions.transitionSpec(from = TestScenes.SceneB, to = TestScenes.SceneA)
+ assertThat(transition.transformations)
+ .comparingElementsUsing(TRANSFORMATION_RANGE)
+ .containsExactly(
+ TransformationRange(start = 1f - 0.8f, end = 1f - 0.1f),
+ TransformationRange(start = 1f - 300 / 500f, end = 1f - 100 / 500f),
+ )
+ }
+
+ companion object {
+ private val TRANSFORMATION_RANGE =
+ Correspondence.transforming<Transformation, TransformationRange?>(
+ { it?.range },
+ "has range equal to"
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
new file mode 100644
index 0000000..2af3638
--- /dev/null
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
@@ -0,0 +1,157 @@
+/*
+ * 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.compose.animation.scene.transformation
+
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.assertPositionInRootIsEqualTo
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.TestElements
+import com.android.compose.animation.scene.TestScenes
+import com.android.compose.animation.scene.inScene
+import com.android.compose.animation.scene.testTransition
+import com.android.compose.modifiers.size
+import com.android.compose.test.assertSizeIsEqualTo
+import com.android.compose.test.onEach
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SharedElementTest {
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun testSharedElement() {
+ rule.testTransition(
+ fromSceneContent = {
+ // Foo is at (10, 50) with a size of (20, 80).
+ Box(Modifier.offset(10.dp, 50.dp).element(TestElements.Foo).size(20.dp, 80.dp))
+ },
+ toSceneContent = {
+ // Foo is at (50, 70) with a size of (10, 40).
+ Box(Modifier.offset(50.dp, 70.dp).element(TestElements.Foo).size(10.dp, 40.dp))
+ },
+ transition = {
+ spec = tween(16 * 4, easing = LinearEasing)
+ // Elements should be shared by default.
+ }
+ ) {
+ before {
+ onElement(TestElements.Foo).assertPositionInRootIsEqualTo(10.dp, 50.dp)
+ onElement(TestElements.Foo).assertSizeIsEqualTo(20.dp, 80.dp)
+ }
+ at(0) {
+ onSharedElement(TestElements.Foo).onEach {
+ assertPositionInRootIsEqualTo(10.dp, 50.dp)
+ assertSizeIsEqualTo(20.dp, 80.dp)
+ }
+ }
+ at(16) {
+ onSharedElement(TestElements.Foo).onEach {
+ assertPositionInRootIsEqualTo(20.dp, 55.dp)
+ assertSizeIsEqualTo(17.5.dp, 70.dp)
+ }
+ }
+ at(32) {
+ onSharedElement(TestElements.Foo).onEach {
+ assertPositionInRootIsEqualTo(30.dp, 60.dp)
+ assertSizeIsEqualTo(15.dp, 60.dp)
+ }
+ }
+ at(48) {
+ onSharedElement(TestElements.Foo).onEach {
+ assertPositionInRootIsEqualTo(40.dp, 65.dp)
+ assertSizeIsEqualTo(12.5.dp, 50.dp)
+ }
+ }
+ after {
+ onElement(TestElements.Foo).assertPositionInRootIsEqualTo(50.dp, 70.dp)
+ onElement(TestElements.Foo).assertSizeIsEqualTo(10.dp, 40.dp)
+ }
+ }
+ }
+
+ @Test
+ fun testSharedElementDisabled() {
+ rule.testTransition(
+ fromScene = TestScenes.SceneA,
+ toScene = TestScenes.SceneB,
+ // The full layout is 100x100.
+ layoutModifier = Modifier.size(100.dp),
+ fromSceneContent = {
+ Box(Modifier.fillMaxSize()) {
+ // Foo is at (10, 50).
+ Box(Modifier.offset(10.dp, 50.dp).element(TestElements.Foo))
+ }
+ },
+ toSceneContent = {
+ Box(Modifier.fillMaxSize()) {
+ // Foo is at (50, 60).
+ Box(Modifier.offset(50.dp, 60.dp).element(TestElements.Foo))
+ }
+ },
+ transition = {
+ spec = tween(16 * 4, easing = LinearEasing)
+
+ // Disable the shared element animation.
+ sharedElement(TestElements.Foo, enabled = false)
+
+ // In SceneA, Foo leaves to the left edge.
+ translate(TestElements.Foo.inScene(TestScenes.SceneA), Edge.Left)
+
+ // In SceneB, Foo comes from the bottom edge.
+ translate(TestElements.Foo.inScene(TestScenes.SceneB), Edge.Bottom)
+ },
+ ) {
+ before { onElement(TestElements.Foo).assertPositionInRootIsEqualTo(10.dp, 50.dp) }
+ at(0) {
+ onElement(TestElements.Foo, scene = TestScenes.SceneA)
+ .assertPositionInRootIsEqualTo(10.dp, 50.dp)
+ onElement(TestElements.Foo, scene = TestScenes.SceneB)
+ .assertPositionInRootIsEqualTo(50.dp, 100.dp)
+ }
+ at(16) {
+ onElement(TestElements.Foo, scene = TestScenes.SceneA)
+ .assertPositionInRootIsEqualTo(7.5.dp, 50.dp)
+ onElement(TestElements.Foo, scene = TestScenes.SceneB)
+ .assertPositionInRootIsEqualTo(50.dp, 90.dp)
+ }
+ at(32) {
+ onElement(TestElements.Foo, scene = TestScenes.SceneA)
+ .assertPositionInRootIsEqualTo(5.dp, 50.dp)
+ onElement(TestElements.Foo, scene = TestScenes.SceneB)
+ .assertPositionInRootIsEqualTo(50.dp, 80.dp)
+ }
+ at(48) {
+ onElement(TestElements.Foo, scene = TestScenes.SceneA)
+ .assertPositionInRootIsEqualTo(2.5.dp, 50.dp)
+ onElement(TestElements.Foo, scene = TestScenes.SceneB)
+ .assertPositionInRootIsEqualTo(50.dp, 70.dp)
+ }
+ after { onElement(TestElements.Foo).assertPositionInRootIsEqualTo(50.dp, 60.dp) }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/test/Selectors.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/test/Selectors.kt
new file mode 100644
index 0000000..d6f64bf
--- /dev/null
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/test/Selectors.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.test
+
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.SemanticsNodeInteractionCollection
+
+/** Assert [assert] on each element of [this] [SemanticsNodeInteractionCollection]. */
+fun SemanticsNodeInteractionCollection.onEach(assert: SemanticsNodeInteraction.() -> Unit) {
+ for (i in 0 until this.fetchSemanticsNodes().size) {
+ get(i).assert()
+ }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index c41dc53..cb76ad7 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -507,9 +507,10 @@
}
}
- private var isVerifying = AtomicBoolean(false)
+ private var isQueued = AtomicBoolean(false)
fun verifyLoadedProviders() {
- val shouldSchedule = isVerifying.compareAndSet(false, true)
+ Log.i(TAG, Thread.currentThread().getStackTrace().toString())
+ val shouldSchedule = isQueued.compareAndSet(false, true)
if (!shouldSchedule) {
logger.tryLog(
TAG,
@@ -521,48 +522,54 @@
}
scope.launch(bgDispatcher) {
- if (keepAllLoaded) {
+ // TODO(b/267372164): Use better threading approach when converting to flows
+ synchronized(availableClocks) {
+ isQueued.set(false)
+ if (keepAllLoaded) {
+ logger.tryLog(
+ TAG,
+ LogLevel.INFO,
+ {},
+ { "verifyLoadedProviders: keepAllLoaded=true" }
+ )
+ // Enforce that all plugins are loaded if requested
+ for ((_, info) in availableClocks) {
+ info.manager?.loadPlugin()
+ }
+ return@launch
+ }
+
+ val currentClock = availableClocks[currentClockId]
+ if (currentClock == null) {
+ logger.tryLog(
+ TAG,
+ LogLevel.INFO,
+ {},
+ { "verifyLoadedProviders: currentClock=null" }
+ )
+ // Current Clock missing, load no plugins and use default
+ for ((_, info) in availableClocks) {
+ info.manager?.unloadPlugin()
+ }
+ return@launch
+ }
+
logger.tryLog(
TAG,
LogLevel.INFO,
{},
- { "verifyLoadedProviders: keepAllLoaded=true" }
+ { "verifyLoadedProviders: load currentClock" }
)
- // Enforce that all plugins are loaded if requested
+ val currentManager = currentClock.manager
+ currentManager?.loadPlugin()
+
for ((_, info) in availableClocks) {
- info.manager?.loadPlugin()
- }
- isVerifying.set(false)
- return@launch
- }
-
- val currentClock = availableClocks[currentClockId]
- if (currentClock == null) {
- logger.tryLog(
- TAG,
- LogLevel.INFO,
- {},
- { "verifyLoadedProviders: currentClock=null" }
- )
- // Current Clock missing, load no plugins and use default
- for ((_, info) in availableClocks) {
- info.manager?.unloadPlugin()
- }
- isVerifying.set(false)
- return@launch
- }
-
- logger.tryLog(TAG, LogLevel.INFO, {}, { "verifyLoadedProviders: load currentClock" })
- val currentManager = currentClock.manager
- currentManager?.loadPlugin()
-
- for ((_, info) in availableClocks) {
- val manager = info.manager
- if (manager != null && currentManager != manager) {
- manager.unloadPlugin()
+ val manager = info.manager
+ if (manager != null && currentManager != manager) {
+ manager.unloadPlugin()
+ }
}
}
- isVerifying.set(false)
}
}
diff --git a/packages/SystemUI/res/layout/connected_display_dialog.xml b/packages/SystemUI/res/layout/connected_display_dialog.xml
index 569dd4c..a51c55e 100644
--- a/packages/SystemUI/res/layout/connected_display_dialog.xml
+++ b/packages/SystemUI/res/layout/connected_display_dialog.xml
@@ -52,7 +52,7 @@
style="@style/Widget.Dialog.Button.BorderButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/cancel" />
+ android:text="@string/dismiss_dialog" />
<Space
android:layout_width="0dp"
@@ -64,6 +64,6 @@
style="@style/Widget.Dialog.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/enable_display" />
+ android:text="@string/mirror_display" />
</LinearLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index 85fb3ac..587caaf 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -44,6 +44,6 @@
<!-- Whether to force split shade.
For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
- TODO (b/293290851) - change this comment/resource when flag is enabled -->
+ TODO (b/293252410) - change this comment/resource when flag is enabled -->
<bool name="force_config_use_split_notification_shade">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index e63229a..fc6d20e 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -38,6 +38,6 @@
<!-- Whether to force split shade.
For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
- TODO (b/293290851) - change this comment/resource when flag is enabled -->
+ TODO (b/293252410) - change this comment/resource when flag is enabled -->
<bool name="force_config_use_split_notification_shade">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b6bca65..18f24ec 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -88,7 +88,7 @@
<!-- The default tiles to display in QuickSettings -->
<string name="quick_settings_tiles_default" translatable="false">
- internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,custom(com.android.permissioncontroller/.permission.service.SafetyCenterQsTileService)
+ internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,custom(com.android.permissioncontroller/.permission.service.v33.SafetyCenterQsTileService)
</string>
<!-- The class path of the Safety Quick Settings Tile -->
@@ -604,7 +604,7 @@
<!-- Whether to force split shade.
For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
- TODO (b/293290851) - change this comment/resource when flag is enabled -->
+ TODO (b/293252410) - change this comment/resource when flag is enabled -->
<bool name="force_config_use_split_notification_shade">false</bool>
<!-- Whether we use large screen shade header which takes only one row compared to QS header -->
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 21696fe..6cdd15e 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -233,6 +233,11 @@
<item type="id" name="pin_pad"/>
<!--
+ Tag used to store pending intent registration listeners in NotificationTemplateViewWrapper
+ -->
+ <item type="id" name="pending_intent_listener_tag" />
+
+ <!--
Used to tag views programmatically added to the smartspace area so they can be more easily
removed later.
-->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5860806..a2637d5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3214,9 +3214,10 @@
<!--- Title of the dialog appearing when an external display is connected, asking whether to start mirroring [CHAR LIMIT=NONE]-->
<string name="connected_display_dialog_start_mirroring">Mirror to external display?</string>
-
<!--- Label of the "enable display" button of the dialog appearing when an external display is connected [CHAR LIMIT=NONE]-->
- <string name="enable_display">Enable display</string>
+ <string name="mirror_display">Mirror display</string>
+ <!--- Label of the dismiss button of the dialog appearing when an external display is connected [CHAR LIMIT=NONE]-->
+ <string name="dismiss_dialog">Dismiss</string>
<!-- Title of the privacy dialog, shown for active / recent app usage of some phone sensors [CHAR LIMIT=30] -->
<string name="privacy_dialog_title">Microphone & Camera</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 4b14d3cf..bf68869 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -37,9 +37,6 @@
* Various shared constants between Launcher and SysUI as part of quickstep
*/
public class QuickStepContract {
- // Fully qualified name of the Launcher activity.
- public static final String LAUNCHER_ACTIVITY_CLASS_NAME =
- "com.google.android.apps.nexuslauncher.NexusLauncherActivity";
public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy";
public static final String KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER = "extra_unfold_animation";
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISysuiUnlockAnimationController.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISysuiUnlockAnimationController.aidl
index cf83f62..31d78b9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISysuiUnlockAnimationController.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISysuiUnlockAnimationController.aidl
@@ -24,7 +24,8 @@
interface ISysuiUnlockAnimationController {
// Provides an implementation of the LauncherUnlockAnimationController to System UI, so that
// SysUI can use it to control the unlock animation in the launcher window.
- oneway void setLauncherUnlockController(ILauncherUnlockAnimationController callback);
+ oneway void setLauncherUnlockController(
+ String activityClass, ILauncherUnlockAnimationController callback);
// Called by Launcher whenever anything happens to change the state of its smartspace. System UI
// proactively saves this and uses it to perform the unlock animation without needing to make a
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index ba8e427..f6a0563 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -184,6 +184,10 @@
}
}
+ public boolean getSplitShadeCentered() {
+ return mSplitShadeCentered;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 29414ea..5646abe 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -234,6 +234,10 @@
}
}
+ public KeyguardClockSwitch getView() {
+ return mView;
+ }
+
private void hideSliceViewAndNotificationIconContainer() {
View ksv = mView.findViewById(R.id.keyguard_slice_view);
ksv.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index b2287d87..51dafac 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -18,6 +18,7 @@
import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC;
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS;
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_NONE_SECURITY;
@@ -68,8 +69,6 @@
import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.res.R;
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor;
import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate;
import com.android.systemui.biometrics.SideFpsController;
import com.android.systemui.biometrics.SideFpsUiRequestSource;
@@ -77,6 +76,7 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
@@ -84,6 +84,7 @@
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -420,7 +421,7 @@
}
};
private final UserInteractor mUserInteractor;
- private final Provider<AuthenticationInteractor> mAuthenticationInteractor;
+ private final Provider<DeviceEntryInteractor> mDeviceEntryInteractor;
private final Provider<JavaAdapter> mJavaAdapter;
private final DeviceProvisionedController mDeviceProvisionedController;
private final Lazy<PrimaryBouncerInteractor> mPrimaryBouncerInteractor;
@@ -457,7 +458,7 @@
FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate,
KeyguardTransitionInteractor keyguardTransitionInteractor,
Lazy<PrimaryBouncerInteractor> primaryBouncerInteractor,
- Provider<AuthenticationInteractor> authenticationInteractor
+ Provider<DeviceEntryInteractor> deviceEntryInteractor
) {
super(view);
view.setAccessibilityDelegate(faceAuthAccessibilityDelegate);
@@ -487,7 +488,7 @@
mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
mBouncerMessageInteractor = bouncerMessageInteractor;
mUserInteractor = userInteractor;
- mAuthenticationInteractor = authenticationInteractor;
+ mDeviceEntryInteractor = deviceEntryInteractor;
mJavaAdapter = javaAdapter;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mDeviceProvisionedController = deviceProvisionedController;
@@ -519,9 +520,9 @@
// When the scene framework says that the lockscreen has been dismissed, dismiss the
// keyguard here, revealing the underlying app or launcher:
mSceneTransitionCollectionJob = mJavaAdapter.get().alwaysCollectFlow(
- mAuthenticationInteractor.get().isLockscreenDismissed(),
- isLockscreenDismissed -> {
- if (isLockscreenDismissed) {
+ mDeviceEntryInteractor.get().isDeviceEntered(),
+ isDeviceEntered -> {
+ if (isDeviceEntered) {
final int selectedUserId = mUserInteractor.getSelectedUserId();
showNextSecurityScreenOrFinish(
/* authenticated= */ true,
@@ -1081,15 +1082,11 @@
* one side).
*/
private boolean canUseOneHandedBouncer() {
- switch(mCurrentSecurityMode) {
- case PIN:
- case Pattern:
- case SimPin:
- case SimPuk:
- return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
- default:
- return false;
- }
+ return switch (mCurrentSecurityMode) {
+ case PIN, Pattern, SimPin, SimPuk -> getResources().getBoolean(
+ R.bool.can_use_one_handed_bouncer);
+ default -> false;
+ };
}
private boolean canDisplayUserSwitcher() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index d848602..f9cc03ee 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -18,6 +18,7 @@
import static java.util.Collections.emptySet;
+import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
@@ -78,6 +79,14 @@
mKeyguardSlice = findViewById(R.id.keyguard_slice_view);
mMediaHostContainer = findViewById(R.id.status_view_media_container);
+ if (mMediaHostContainer != null) {
+ LayoutTransition mediaLayoutTransition = new LayoutTransition();
+ ((ViewGroup) mMediaHostContainer).setLayoutTransition(mediaLayoutTransition);
+ mediaLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
+ mediaLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+ mediaLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
+ mediaLayoutTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
+ }
updateDark();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 8d0d299..931ba6d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -23,6 +23,7 @@
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.animation.Animator;
+import android.animation.LayoutTransition;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.res.Configuration;
@@ -101,6 +102,7 @@
private final Rect mClipBounds = new Rect();
private final KeyguardInteractor mKeyguardInteractor;
+ private Boolean mSplitShadeEnabled = false;
private Boolean mStatusViewCentered = true;
private DumpManager mDumpManager;
@@ -150,6 +152,48 @@
@Override
public void onInit() {
mKeyguardClockSwitchController.init();
+ final View mediaHostContainer = mView.findViewById(R.id.status_view_media_container);
+ if (mediaHostContainer != null) {
+ mKeyguardClockSwitchController.getView().addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ if (!mSplitShadeEnabled
+ || mKeyguardClockSwitchController.getView().getSplitShadeCentered()
+ // Note: isKeyguardVisible() returns false after Launcher -> AOD.
+ || !mKeyguardUpdateMonitor.isKeyguardVisible()) {
+ return;
+ }
+
+ int oldHeight = oldBottom - oldTop;
+ if (v.getHeight() == oldHeight) return;
+
+ if (mediaHostContainer.getVisibility() != View.VISIBLE
+ // If the media is appearing, also don't do the transition.
+ || mediaHostContainer.getHeight() == 0) {
+ return;
+ }
+
+ final LayoutTransition mediaLayoutTransition =
+ ((ViewGroup) mediaHostContainer).getLayoutTransition();
+ if (mediaLayoutTransition == null) return;
+
+ mediaLayoutTransition.enableTransitionType(LayoutTransition.CHANGING);
+ });
+
+ mediaHostContainer.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ final LayoutTransition mediaLayoutTransition =
+ ((ViewGroup) mediaHostContainer).getLayoutTransition();
+ if (mediaLayoutTransition == null) return;
+ if (!mediaLayoutTransition.isTransitionTypeEnabled(
+ LayoutTransition.CHANGING)) {
+ return;
+ }
+ // Note: when this is called, the LayoutTransition is already been set up.
+ // Disables the LayoutTransition until it's explicitly enabled again.
+ mediaLayoutTransition.disableTransitionType(LayoutTransition.CHANGING);
+ }
+ );
+ }
mDumpManager.registerDumpable(getInstanceName(), this);
if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
@@ -385,6 +429,7 @@
*/
public void setSplitShadeEnabled(boolean enabled) {
mKeyguardClockSwitchController.setSplitShadeEnabled(enabled);
+ mSplitShadeEnabled = enabled;
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 165c4bb..a81069a 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -424,11 +424,11 @@
private void updateConfiguration() {
WindowManager windowManager = mContext.getSystemService(WindowManager.class);
Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
- WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets();
mWidthPixels = bounds.right;
if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
// Assumed to be initially neglected as there are no left or right insets in portrait
// However, on landscape, these insets need to included when calculating the midpoint
+ WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets();
mWidthPixels -= insets.getSystemWindowInsetLeft() + insets.getSystemWindowInsetRight();
}
mHeightPixels = bounds.bottom;
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 954129e..22bd207 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -18,8 +18,8 @@
import static androidx.dynamicanimation.animation.DynamicAnimation.TRANSLATION_X;
import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat;
-
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
+import static com.android.systemui.flags.Flags.SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX;
import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
import android.animation.Animator;
@@ -482,7 +482,14 @@
boolean wasRemoved = false;
if (animView instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
- wasRemoved = row.isRemoved();
+ if (mFeatureFlags.isEnabled(SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX)) {
+ // If the view is already removed from its parent and added as Transient,
+ // we need to clean the transient view upon animation end
+ wasRemoved = row.getTransientContainer() != null
+ || row.getParent() == null || row.isRemoved();
+ } else {
+ wasRemoved = row.isRemoved();
+ }
}
if (!mCancelled || wasRemoved) {
mCallback.onChildDismissed(animView);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index c095aa8..584357b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -80,8 +80,8 @@
import com.android.internal.accessibility.common.MagnificationConstants;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.res.R;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.res.R;
import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
@@ -205,7 +205,7 @@
private final Supplier<IWindowSession> mGlobalWindowSessionSupplier;
private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
private final MagnificationGestureDetector mGestureDetector;
- private final int mBounceEffectDuration;
+ private int mBounceEffectDuration;
private final Choreographer.FrameCallback mMirrorViewGeometryVsyncCallback;
private Locale mLocale;
private NumberFormat mPercentFormat;
@@ -272,8 +272,8 @@
setupMagnificationSizeScaleOptions();
- mBounceEffectDuration = mResources.getInteger(
- com.android.internal.R.integer.config_shortAnimTime);
+ setBounceEffectDuration(mResources.getInteger(
+ com.android.internal.R.integer.config_shortAnimTime));
updateDimensions();
final Size windowFrameSize = restoreMagnificationWindowFrameSizeIfPossible();
@@ -1461,6 +1461,11 @@
mDragView.setColorFilter(filter);
}
+ @VisibleForTesting
+ void setBounceEffectDuration(int duration) {
+ mBounceEffectDuration = duration;
+ }
+
private void animateBounceEffect() {
final ObjectAnimator scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(mMirrorView,
PropertyValuesHolder.ofFloat(View.SCALE_X, 1, mBounceEffectAnimationScale, 1),
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index b2433d4..80be008 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -29,9 +29,9 @@
import com.android.systemui.authentication.shared.model.AuthenticationResultModel
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.time.SystemClock
@@ -59,18 +59,6 @@
/** Defines interface for classes that can access authentication-related application state. */
interface AuthenticationRepository {
-
- /**
- * Whether the device is unlocked.
- *
- * A device that is not yet unlocked requires unlocking by completing an authentication
- * challenge according to the current authentication method, unless in cases when the current
- * authentication method is not "secure" (for example, None); in such cases, the value of this
- * flow will always be `true`, even if the lockscreen is showing and still needs to be dismissed
- * by the user to proceed.
- */
- val isUnlocked: StateFlow<Boolean>
-
/**
* Whether the auto confirm feature is enabled for the currently-selected user.
*
@@ -129,14 +117,6 @@
/** Returns the length of the PIN or `0` if the current auth method is not PIN. */
suspend fun getPinLength(): Int
- /**
- * Returns whether the lockscreen is enabled.
- *
- * When the lockscreen is not enabled, it shouldn't show in cases when the authentication method
- * is considered not secure (for example, "swipe" is considered to be "none").
- */
- suspend fun isLockscreenEnabled(): Boolean
-
/** Reports an authentication attempt. */
suspend fun reportAuthenticationAttempt(isSuccessful: Boolean)
@@ -167,6 +147,7 @@
suspend fun checkCredential(credential: LockscreenCredential): AuthenticationResultModel
}
+@SysUISingleton
class AuthenticationRepositoryImpl
@Inject
constructor(
@@ -174,20 +155,10 @@
private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val userRepository: UserRepository,
- keyguardRepository: KeyguardRepository,
private val lockPatternUtils: LockPatternUtils,
broadcastDispatcher: BroadcastDispatcher,
) : AuthenticationRepository {
- override val isUnlocked = keyguardRepository.isKeyguardUnlocked
-
- override suspend fun isLockscreenEnabled(): Boolean {
- return withContext(backgroundDispatcher) {
- val selectedUserId = userRepository.selectedUserId
- !lockPatternUtils.isLockScreenDisabled(selectedUserId)
- }
- }
-
override val isAutoConfirmEnabled: StateFlow<Boolean> =
refreshingFlow(
initialValue = false,
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 4cfc6aa..453a7a6 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -26,9 +26,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -42,15 +40,19 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-/** Hosts application business logic related to authentication. */
+/**
+ * Hosts application business logic related to user authentication.
+ *
+ * Note: there is a distinction between authentication (determining a user's identity) and device
+ * entry (dismissing the lockscreen). For logic that is specific to device entry, please use
+ * `DeviceEntryInteractor` instead.
+ */
@SysUISingleton
class AuthenticationInteractor
@Inject
@@ -59,8 +61,7 @@
private val repository: AuthenticationRepository,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val userRepository: UserRepository,
- private val keyguardRepository: KeyguardRepository,
- sceneInteractor: SceneInteractor,
+ private val deviceEntryRepository: DeviceEntryRepository,
private val clock: SystemClock,
) {
/**
@@ -77,76 +78,13 @@
* Note: this layer adds the synthetic authentication method of "swipe" which is special. When
* the current authentication method is "swipe", the user does not need to complete any
* authentication challenge to unlock the device; they just need to dismiss the lockscreen to
- * get past it. This also means that the value of [isUnlocked] remains `false` even when the
- * lockscreen is showing and still needs to be dismissed by the user to proceed.
+ * get past it. This also means that the value of `DeviceEntryInteractor#isUnlocked` remains
+ * `true` even when the lockscreen is showing and still needs to be dismissed by the user to
+ * proceed.
*/
val authenticationMethod: Flow<DomainLayerAuthenticationMethodModel> =
repository.authenticationMethod.map { rawModel -> rawModel.toDomainLayer() }
- /**
- * Whether the device is unlocked.
- *
- * A device that is not yet unlocked requires unlocking by completing an authentication
- * challenge according to the current authentication method, unless in cases when the current
- * authentication method is not "secure" (for example, None and Swipe); in such cases, the value
- * of this flow will always be `true`, even if the lockscreen is showing and still needs to be
- * dismissed by the user to proceed.
- */
- val isUnlocked: StateFlow<Boolean> =
- combine(
- repository.isUnlocked,
- authenticationMethod,
- ) { isUnlocked, authenticationMethod ->
- !authenticationMethod.isSecure || isUnlocked
- }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.Eagerly,
- initialValue = false,
- )
-
- /**
- * Whether the lockscreen has been dismissed (by any method). This can be false even when the
- * device is unlocked, e.g. when swipe to unlock is enabled.
- *
- * Note:
- * - `false` doesn't mean the lockscreen is visible (it may be occluded or covered by other UI).
- * - `true` doesn't mean the lockscreen is invisible (since this state changes before the
- * transition occurs).
- */
- val isLockscreenDismissed: StateFlow<Boolean> =
- sceneInteractor.desiredScene
- .map { it.key }
- .filter { currentScene ->
- currentScene == SceneKey.Gone || currentScene == SceneKey.Lockscreen
- }
- .map { it == SceneKey.Gone }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = false,
- )
-
- /**
- * Whether it's currently possible to swipe up to dismiss the lockscreen without requiring
- * authentication. This returns false whenever the lockscreen has been dismissed.
- *
- * Note: `true` doesn't mean the lockscreen is visible. It may be occluded or covered by other
- * UI.
- */
- val canSwipeToDismiss =
- combine(authenticationMethod, isLockscreenDismissed) {
- authenticationMethod,
- isLockscreenDismissed ->
- authenticationMethod is DomainLayerAuthenticationMethodModel.Swipe &&
- !isLockscreenDismissed
- }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = false,
- )
-
/** The current authentication throttling state, only meaningful if [isThrottled] is `true`. */
val throttling: StateFlow<AuthenticationThrottlingModel> = repository.throttling
@@ -211,32 +149,15 @@
* Note: this layer adds the synthetic authentication method of "swipe" which is special. When
* the current authentication method is "swipe", the user does not need to complete any
* authentication challenge to unlock the device; they just need to dismiss the lockscreen to
- * get past it. This also means that the value of [isUnlocked] remains `false` even when the
- * lockscreen is showing and still needs to be dismissed by the user to proceed.
+ * get past it. This also means that the value of `DeviceEntryInteractor#isUnlocked` remains
+ * `true` even when the lockscreen is showing and still needs to be dismissed by the user to
+ * proceed.
*/
suspend fun getAuthenticationMethod(): DomainLayerAuthenticationMethodModel {
return repository.getAuthenticationMethod().toDomainLayer()
}
/**
- * Returns `true` if the device currently requires authentication before content can be viewed;
- * `false` if content can be displayed without unlocking first.
- */
- suspend fun isAuthenticationRequired(): Boolean {
- return !isUnlocked.value && getAuthenticationMethod().isSecure
- }
-
- /**
- * Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
- * dismisses once the authentication challenge is completed. For example, completing a biometric
- * authentication challenge via face unlock or fingerprint sensor can automatically bypass the
- * lock screen.
- */
- fun isBypassEnabled(): Boolean {
- return keyguardRepository.isBypassEnabled()
- }
-
- /**
* Attempts to authenticate the user and unlock the device.
*
* If [tryAutoConfirm] is `true`, authentication is attempted if and only if the auth method
@@ -312,7 +233,7 @@
/** Starts refreshing the throttling state every second. */
private suspend fun startThrottlingCountdown() {
- cancelCountdown()
+ cancelThrottlingCountdown()
throttlingCountdownJob =
applicationScope.launch {
while (refreshThrottling() > 0) {
@@ -322,14 +243,14 @@
}
/** Cancels any throttling state countdown started in [startThrottlingCountdown]. */
- private fun cancelCountdown() {
+ private fun cancelThrottlingCountdown() {
throttlingCountdownJob?.cancel()
throttlingCountdownJob = null
}
/** Notifies that the currently-selected user has changed. */
private suspend fun onSelectedUserChanged() {
- cancelCountdown()
+ cancelThrottlingCountdown()
if (refreshThrottling() > 0) {
startThrottlingCountdown()
}
@@ -378,7 +299,7 @@
DomainLayerAuthenticationMethodModel {
return when (this) {
is DataLayerAuthenticationMethodModel.None ->
- if (repository.isLockscreenEnabled()) {
+ if (deviceEntryRepository.isInsecureLockscreenEnabled()) {
DomainLayerAuthenticationMethodModel.Swipe
} else {
DomainLayerAuthenticationMethodModel.None
@@ -394,13 +315,10 @@
/** Result of a user authentication attempt. */
enum class AuthenticationResult {
- /** Authentication succeeded and the device is now unlocked. */
+ /** Authentication succeeded. */
SUCCEEDED,
- /** Authentication failed and the device remains unlocked. */
+ /** Authentication failed. */
FAILED,
- /**
- * Authentication was not performed, e.g. due to insufficient input, and the device remains
- * unlocked.
- */
+ /** Authentication was not performed, e.g. due to insufficient input. */
SKIPPED,
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index f3a463b..0c02369 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -26,6 +26,7 @@
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
@@ -50,6 +51,7 @@
@Application private val applicationScope: CoroutineScope,
@Application private val applicationContext: Context,
private val repository: BouncerRepository,
+ private val deviceEntryInteractor: DeviceEntryInteractor,
private val authenticationInteractor: AuthenticationInteractor,
private val sceneInteractor: SceneInteractor,
flags: SceneContainerFlags,
@@ -144,7 +146,7 @@
message: String? = null,
) {
applicationScope.launch {
- if (authenticationInteractor.isAuthenticationRequired()) {
+ if (deviceEntryInteractor.isAuthenticationRequired()) {
repository.setMessage(
message ?: promptMessage(authenticationInteractor.getAuthenticationMethod())
)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 782ead3..c98cf31 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -62,6 +62,10 @@
initialValue = !bouncerInteractor.isThrottled.value,
)
+ // Handle to the scope of the child ViewModel (stored in [authMethod]).
+ private var childViewModelScope: CoroutineScope? = null
+ private val _throttlingDialogMessage = MutableStateFlow<String?>(null)
+
/** View-model for the current UI, based on the current authentication method. */
val authMethodViewModel: StateFlow<AuthMethodBouncerViewModel?> =
authenticationInteractor.authenticationMethod
@@ -72,8 +76,31 @@
initialValue = null,
)
- // Handle to the scope of the child ViewModel (stored in [authMethod]).
- private var childViewModelScope: CoroutineScope? = null
+ /**
+ * A message for a throttling dialog to show when the user has attempted the wrong credential
+ * too many times and now must wait a while before attempting again.
+ *
+ * If `null`, no dialog should be shown.
+ *
+ * Once the dialog is shown, the UI should call [onThrottlingDialogDismissed] when the user
+ * dismisses this dialog.
+ */
+ val throttlingDialogMessage: StateFlow<String?> = _throttlingDialogMessage.asStateFlow()
+
+ /** The user-facing message to show in the bouncer. */
+ val message: StateFlow<MessageViewModel> =
+ combine(bouncerInteractor.message, bouncerInteractor.isThrottled) { message, isThrottled ->
+ toMessageViewModel(message, isThrottled)
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue =
+ toMessageViewModel(
+ message = bouncerInteractor.message.value,
+ isThrottled = bouncerInteractor.isThrottled.value,
+ ),
+ )
init {
if (flags.isEnabled()) {
@@ -98,33 +125,6 @@
}
}
- /** The user-facing message to show in the bouncer. */
- val message: StateFlow<MessageViewModel> =
- combine(bouncerInteractor.message, bouncerInteractor.isThrottled) { message, isThrottled ->
- toMessageViewModel(message, isThrottled)
- }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue =
- toMessageViewModel(
- message = bouncerInteractor.message.value,
- isThrottled = bouncerInteractor.isThrottled.value,
- ),
- )
-
- private val _throttlingDialogMessage = MutableStateFlow<String?>(null)
- /**
- * A message for a throttling dialog to show when the user has attempted the wrong credential
- * too many times and now must wait a while before attempting again.
- *
- * If `null`, no dialog should be shown.
- *
- * Once the dialog is shown, the UI should call [onThrottlingDialogDismissed] when the user
- * dismisses this dialog.
- */
- val throttlingDialogMessage: StateFlow<String?> = _throttlingDialogMessage.asStateFlow()
-
/** Notifies that the emergency services button was clicked. */
fun onEmergencyServicesButtonClicked() {
// TODO(b/280877228): implement this
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 9b93522..e8a8444 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -18,26 +18,25 @@
import android.app.Activity
import android.app.ActivityOptions
-import android.app.ActivityTaskManager
-import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
import android.app.Dialog
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
-import android.graphics.Rect
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import android.view.WindowInsets.Type
import android.view.WindowManager
import android.widget.ImageView
+import androidx.annotation.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.systemui.res.R
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.boundsOnScreen
import com.android.wm.shell.taskview.TaskView
/**
@@ -65,8 +64,8 @@
private const val EXTRA_USE_PANEL = "controls.DISPLAY_IN_PANEL"
}
- var detailTaskId = INVALID_TASK_ID
private lateinit var taskViewContainer: View
+ private lateinit var controlDetailRoot: View
private val taskWidthPercentWidth = activityContext.resources.getFloat(
R.dimen.controls_task_view_width_percentage
)
@@ -79,12 +78,7 @@
addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
}
- fun removeDetailTask() {
- if (detailTaskId == INVALID_TASK_ID) return
- ActivityTaskManager.getInstance().removeTask(detailTaskId)
- detailTaskId = INVALID_TASK_ID
- }
-
+ @VisibleForTesting
val stateCallback = object : TaskView.Listener {
override fun onInitialized() {
taskViewContainer.apply {
@@ -98,33 +92,29 @@
activityContext,
0 /* enterResId */,
0 /* exitResId */
- ).setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
- options.isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
+ ).apply {
+ pendingIntentBackgroundActivityStartMode = MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
+ taskAlwaysOnTop = true
+ }
taskView.startActivity(
pendingIntent,
fillInIntent,
options,
- getTaskViewBounds()
+ taskView.boundsOnScreen,
)
}
override fun onTaskRemovalStarted(taskId: Int) {
- detailTaskId = INVALID_TASK_ID
- dismiss()
+ taskView.release()
}
override fun onTaskCreated(taskId: Int, name: ComponentName?) {
- detailTaskId = taskId
requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
setAlpha(1f)
}
}
-
- override fun onReleased() {
- removeDetailTask()
- }
-
override fun onBackPressedOnTaskRoot(taskId: Int) {
dismiss()
}
@@ -138,6 +128,9 @@
setContentView(R.layout.controls_detail_dialog)
taskViewContainer = requireViewById<ViewGroup>(R.id.control_task_view_container)
+ controlDetailRoot = requireViewById<View>(R.id.control_detail_root).apply {
+ setOnClickListener { _: View -> dismiss() }
+ }
requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
addView(taskView)
@@ -147,13 +140,9 @@
requireViewById<ImageView>(R.id.control_detail_close).apply {
setOnClickListener { _: View -> dismiss() }
}
- requireViewById<View>(R.id.control_detail_root).apply {
- setOnClickListener { _: View -> dismiss() }
- }
requireViewById<ImageView>(R.id.control_detail_open_in_app).apply {
setOnClickListener { v: View ->
- removeDetailTask()
dismiss()
val action = ActivityStarter.OnDismissAction {
@@ -201,26 +190,9 @@
taskView.setListener(cvh.uiExecutor, stateCallback)
}
- fun getTaskViewBounds(): Rect {
- val wm = checkNotNull(context.getSystemService(WindowManager::class.java))
- val windowMetrics = wm.getCurrentWindowMetrics()
- val rect = windowMetrics.bounds
- val metricInsets = windowMetrics.windowInsets
- val insets = metricInsets.getInsetsIgnoringVisibility(Type.systemBars()
- or Type.displayCutout())
- val headerHeight = context.resources.getDimensionPixelSize(
- R.dimen.controls_detail_dialog_header_height)
-
- val finalRect = Rect(rect.left - insets.left /* left */,
- rect.top + insets.top + headerHeight /* top */,
- rect.right - insets.right /* right */,
- rect.bottom - insets.bottom /* bottom */)
- return finalRect
- }
-
override fun dismiss() {
if (!isShowing()) return
- taskView.release()
+ taskView.removeTask()
val isActivityFinishing =
(activityContext as? Activity)?.let { it.isFinishing || it.isDestroyed }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
index 1b0d032..848c786 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
@@ -18,7 +18,6 @@
package com.android.systemui.controls.ui
import android.app.ActivityOptions
-import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
@@ -45,8 +44,6 @@
taskView.alpha = 0f
}
- private var detailTaskId = INVALID_TASK_ID
-
private val fillInIntent =
Intent().apply {
// Apply flags to make behaviour match documentLaunchMode=always.
@@ -57,7 +54,6 @@
private val stateCallback =
object : TaskView.Listener {
override fun onInitialized() {
-
val options =
ActivityOptions.makeCustomAnimation(
activityContext,
@@ -88,12 +84,10 @@
}
override fun onTaskRemovalStarted(taskId: Int) {
- detailTaskId = INVALID_TASK_ID
release()
}
override fun onTaskCreated(taskId: Int, name: ComponentName?) {
- detailTaskId = taskId
taskView.alpha = 1f
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index da5e933..04a9cae 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -25,6 +25,7 @@
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.app.INotificationManager;
+import android.app.IUriGrantsManager;
import android.app.IWallpaperManager;
import android.app.KeyguardManager;
import android.app.NotificationManager;
@@ -688,4 +689,12 @@
static StatusBarManager provideStatusBarManager(Context context) {
return context.getSystemService(StatusBarManager.class);
}
+
+ @Provides
+ @Singleton
+ static IUriGrantsManager provideIUriGrantsManager() {
+ return IUriGrantsManager.Stub.asInterface(
+ ServiceManager.getService(Context.URI_GRANTS_SERVICE)
+ );
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 283a07b..4b6ad6d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -48,6 +48,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.SystemUser;
import com.android.systemui.demomode.dagger.DemoModeModule;
+import com.android.systemui.deviceentry.DeviceEntryModule;
import com.android.systemui.display.DisplayModule;
import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.dreams.dagger.DreamModule;
@@ -173,6 +174,7 @@
ControlsModule.class,
CoroutinesModule.class,
DemoModeModule.class,
+ DeviceEntryModule.class,
DisableFlagsModule.class,
DisplayModule.class,
DreamModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
new file mode 100644
index 0000000..e7f835f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
@@ -0,0 +1,12 @@
+package com.android.systemui.deviceentry
+
+import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule
+import dagger.Module
+
+@Module(
+ includes =
+ [
+ DeviceEntryRepositoryModule::class,
+ ],
+)
+object DeviceEntryModule
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
new file mode 100644
index 0000000..5b85ad0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
@@ -0,0 +1,125 @@
+package com.android.systemui.deviceentry.data.repository
+
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.data.repository.UserRepository
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
+
+/** Interface for classes that can access device-entry-related application state. */
+interface DeviceEntryRepository {
+ /**
+ * Whether the device is unlocked.
+ *
+ * A device that is not yet unlocked requires unlocking by completing an authentication
+ * challenge according to the current authentication method, unless in cases when the current
+ * authentication method is not "secure" (for example, None); in such cases, the value of this
+ * flow will always be `true`, even if the lockscreen is showing and still needs to be dismissed
+ * by the user to proceed.
+ */
+ val isUnlocked: StateFlow<Boolean>
+
+ /**
+ * Whether the lockscreen should be shown when the authentication method is not secure (e.g.
+ * `None` or `Swipe`).
+ */
+ suspend fun isInsecureLockscreenEnabled(): Boolean
+
+ /**
+ * Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically
+ * dismissed once the authentication challenge is completed.
+ *
+ * This is a setting that is specific to the face unlock authentication method, because the user
+ * intent to unlock is not known. On devices that don't support face unlock, this always returns
+ * `true`.
+ *
+ * When this is `false`, an automatically-triggered face unlock shouldn't automatically dismiss
+ * the lockscreen.
+ */
+ fun isBypassEnabled(): Boolean
+}
+
+/** Encapsulates application state for device entry. */
+@SysUISingleton
+class DeviceEntryRepositoryImpl
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val userRepository: UserRepository,
+ private val lockPatternUtils: LockPatternUtils,
+ private val keyguardBypassController: KeyguardBypassController,
+ keyguardStateController: KeyguardStateController,
+) : DeviceEntryRepository {
+
+ override val isUnlocked =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val callback =
+ object : KeyguardStateController.Callback {
+ override fun onUnlockedChanged() {
+ trySendWithFailureLogging(
+ keyguardStateController.isUnlocked,
+ TAG,
+ "updated isUnlocked due to onUnlockedChanged"
+ )
+ }
+
+ override fun onKeyguardShowingChanged() {
+ trySendWithFailureLogging(
+ keyguardStateController.isUnlocked,
+ TAG,
+ "updated isUnlocked due to onKeyguardShowingChanged"
+ )
+ }
+ }
+
+ keyguardStateController.addCallback(callback)
+ // Adding the callback does not send an initial update.
+ trySendWithFailureLogging(
+ keyguardStateController.isUnlocked,
+ TAG,
+ "initial isKeyguardUnlocked"
+ )
+
+ awaitClose { keyguardStateController.removeCallback(callback) }
+ }
+ .distinctUntilChanged()
+ .stateIn(
+ applicationScope,
+ SharingStarted.Eagerly,
+ initialValue = false,
+ )
+
+ override suspend fun isInsecureLockscreenEnabled(): Boolean {
+ return withContext(backgroundDispatcher) {
+ val selectedUserId = userRepository.getSelectedUserInfo().id
+ !lockPatternUtils.isLockScreenDisabled(selectedUserId)
+ }
+ }
+
+ override fun isBypassEnabled() = keyguardBypassController.bypassEnabled
+
+ companion object {
+ private const val TAG = "DeviceEntryRepositoryImpl"
+ }
+}
+
+@Module
+interface DeviceEntryRepositoryModule {
+ @Binds fun repository(impl: DeviceEntryRepositoryImpl): DeviceEntryRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
new file mode 100644
index 0000000..5612c9a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -0,0 +1,110 @@
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.SceneKey
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Hosts application business logic related to device entry.
+ *
+ * Device entry occurs when the user successfully dismisses (or bypasses) the lockscreen, regardless
+ * of the authentication method used.
+ */
+@SysUISingleton
+class DeviceEntryInteractor
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val repository: DeviceEntryRepository,
+ private val authenticationInteractor: AuthenticationInteractor,
+ sceneInteractor: SceneInteractor,
+) {
+ /**
+ * Whether the device is unlocked.
+ *
+ * A device that is not yet unlocked requires unlocking by completing an authentication
+ * challenge according to the current authentication method, unless in cases when the current
+ * authentication method is not "secure" (for example, None and Swipe); in such cases, the value
+ * of this flow will always be `true`, even if the lockscreen is showing and still needs to be
+ * dismissed by the user to proceed.
+ */
+ val isUnlocked: StateFlow<Boolean> =
+ combine(
+ repository.isUnlocked,
+ authenticationInteractor.authenticationMethod,
+ ) { isUnlocked, authenticationMethod ->
+ !authenticationMethod.isSecure || isUnlocked
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ )
+
+ /**
+ * Whether the device has been entered (i.e. the lockscreen has been dismissed, by any method).
+ * This can be `false` when the device is unlocked, e.g. when the user still needs to swipe away
+ * the non-secure lockscreen, even though they've already authenticated.
+ *
+ * Note: This does not imply that the lockscreen is visible or not.
+ */
+ val isDeviceEntered: StateFlow<Boolean> =
+ sceneInteractor.desiredScene
+ .map { it.key }
+ .filter { currentScene ->
+ currentScene == SceneKey.Gone || currentScene == SceneKey.Lockscreen
+ }
+ .map { it == SceneKey.Gone }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ /**
+ * Whether it's currently possible to swipe up to enter the device without requiring
+ * authentication. This returns `false` whenever the lockscreen has been dismissed.
+ *
+ * Note: `true` doesn't mean the lockscreen is visible. It may be occluded or covered by other
+ * UI.
+ */
+ val canSwipeToEnter =
+ combine(authenticationInteractor.authenticationMethod, isDeviceEntered) {
+ authenticationMethod,
+ isDeviceEntered ->
+ authenticationMethod is AuthenticationMethodModel.Swipe && !isDeviceEntered
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ /**
+ * Returns `true` if the device currently requires authentication before entry is granted;
+ * `false` if the device can be entered without authenticating first.
+ */
+ suspend fun isAuthenticationRequired(): Boolean {
+ return !isUnlocked.value && authenticationInteractor.getAuthenticationMethod().isSecure
+ }
+
+ /**
+ * Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
+ * dismissed once the authentication challenge is completed. For example, completing a biometric
+ * authentication challenge via face unlock or fingerprint sensor can automatically bypass the
+ * lock screen.
+ */
+ fun isBypassEnabled() = repository.isBypassEnabled()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 3b70555..a00c3b5 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -80,16 +80,9 @@
@JvmField
val NOTIFICATION_INLINE_REPLY_ANIMATION = releasedFlag("notification_inline_reply_animation")
- /** Makes sure notification panel is updated before the user switch is complete. */
- // TODO(b/278873737): Tracking Bug
- @JvmField
- val LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE =
- releasedFlag("load_notifications_before_the_user_switch_is_complete")
-
// TODO(b/277338665): Tracking Bug
@JvmField
- val NOTIFICATION_SHELF_REFACTOR =
- unreleasedFlag("notification_shelf_refactor", teamfood = true)
+ val NOTIFICATION_SHELF_REFACTOR = releasedFlag("notification_shelf_refactor")
// TODO(b/290787599): Tracking Bug
@JvmField
@@ -303,6 +296,11 @@
@JvmField val MIGRATE_CLOCKS_TO_BLUEPRINT =
unreleasedFlag("migrate_clocks_to_blueprint")
+ /** Migrate KeyguardRootView to use composables. */
+ // TODO(b/301969856): Tracking Bug.
+ @JvmField val KEYGUARD_ROOT_VIEW_USE_COMPOSE =
+ unreleasedFlag("keyguard_root_view_use_compose")
+
/** Enables preview loading animation in the wallpaper picker. */
// TODO(b/274443705): Tracking Bug
@JvmField
@@ -771,6 +769,10 @@
// TODO(b/302087895): Tracking Bug
@JvmField val CALL_LAYOUT_ASYNC_SET_DATA = unreleasedFlag("call_layout_async_set_data")
+ // TODO(b/302144438): Tracking Bug
+ @JvmField val DECOUPLE_REMOTE_INPUT_DELEGATE_AND_CALLBACK_UPDATE =
+ unreleasedFlag("decouple_remote_input_delegate_and_callback_update")
+
// 2900 - CentralSurfaces-related flags
// TODO(b/285174336): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
index d9b2c39..d89cf63 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
@@ -176,7 +176,6 @@
SliderState.DRAG_HANDLE_REACHED_BOOKEND -> executeOnBookend()
SliderState.JUMP_TRACK_LOCATION_SELECTED ->
sliderListener.onProgressJump(latestProgress)
- SliderState.JUMP_BOOKEND_SELECTED -> executeOnBookend()
else -> {}
}
}
@@ -197,7 +196,7 @@
epsilon: Float = 0.00001f,
): Boolean {
val delta = abs(currentProgress - latestProgress)
- return abs(delta - config.jumpThreshold) < epsilon
+ return delta > config.jumpThreshold - epsilon
}
private fun bookendReached(currentProgress: Float): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateListener.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateListener.kt
index 9c99c90..9f12f3f 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateListener.kt
@@ -37,7 +37,9 @@
* Notification that the slider reached a certain progress on the slider track.
*
* This method is called in all intermediate steps of a continuous progress change as the slider
- * moves through the slider track.
+ * moves through the slider track. A single discrete movement of the handle by an external
+ * button or by a jump on the slider track will not trigger this callback. See
+ * [onSelectAndArrow] and [onProgressJump] for these cases.
*
* @param[progress] The progress of the slider in the range from 0F to 1F (inclusive).
*/
@@ -56,7 +58,7 @@
fun onProgressJump(@FloatRange(from = 0.0, to = 1.0) progress: Float)
/**
- * Notification that the slider handle was moved by a button press.
+ * Notification that the slider handle was moved discretely by one step via a button press.
*
* @param[progress] The progress of the slider in the range from 0F to 1F (inclusive).
*/
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index ff74050..2a69ec5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -44,7 +44,6 @@
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.shared.recents.utilities.Utilities
import com.android.systemui.shared.system.ActivityManagerWrapper
-import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController
import com.android.systemui.shared.system.smartspace.SmartspaceState
@@ -219,6 +218,11 @@
*/
private var launcherUnlockController: ILauncherUnlockAnimationController? = null
+ /**
+ * Fully qualified class name of the launcher activity
+ */
+ private var launcherActivityClass: String? = null
+
private val listeners = ArrayList<KeyguardUnlockAnimationListener>()
/**
@@ -226,7 +230,11 @@
* doesn't happen, we won't use in-window animations or the smartspace shared element
* transition, but that's okay!
*/
- override fun setLauncherUnlockController(callback: ILauncherUnlockAnimationController?) {
+ override fun setLauncherUnlockController(
+ activityClass: String,
+ callback: ILauncherUnlockAnimationController?
+ ) {
+ launcherActivityClass = activityClass
launcherUnlockController = callback
}
@@ -545,7 +553,6 @@
// gesture and the surface behind the keyguard should be made visible so that we can animate
// it in.
if (requestedShowSurfaceBehindKeyguard) {
-
// If we're flinging to dismiss here, it means the touch gesture ended in a fling during
// the time it takes the keyguard exit animation to start. This is an edge case race
// condition, which we handle by just playing a canned animation on the now-visible
@@ -785,7 +792,6 @@
if (dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
!keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
-
keyguardViewMediator.get().showSurfaceBehindKeyguard()
} else if (dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
@@ -1113,17 +1119,18 @@
return playingCannedUnlockAnimation
}
+ /**
+ * Return whether the Google Nexus launcher is underneath the keyguard, vs. some other
+ * launcher or an app. If so, we can communicate with it to perform in-window/shared element
+ * transitions!
+ */
+ fun isNexusLauncherUnderneath(): Boolean {
+ return launcherActivityClass?.let { ActivityManagerWrapper.getInstance()
+ .runningTask?.topActivity?.className?.equals(it) }
+ ?: false
+ }
+
companion object {
- /**
- * Return whether the Google Nexus launcher is underneath the keyguard, vs. some other
- * launcher or an app. If so, we can communicate with it to perform in-window/shared element
- * transitions!
- */
- fun isNexusLauncherUnderneath(): Boolean {
- return ActivityManagerWrapper.getInstance()
- .runningTask?.topActivity?.className?.equals(
- QuickStepContract.LAUNCHER_ACTIVITY_CLASS_NAME) ?: false
- }
fun isFoldable(context: Context): Boolean {
return context.resources.getIntArray(R.array.config_foldedDeviceStates).isNotEmpty()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index fb02c7d..1761ca8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -26,7 +26,6 @@
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -40,7 +39,9 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
+import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
@@ -69,6 +70,7 @@
private val context: Context,
private val keyguardIndicationController: KeyguardIndicationController,
private val lockIconViewController: LockIconViewController,
+ private val shadeInteractor: ShadeInteractor,
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -135,6 +137,7 @@
occludingAppDeviceEntryMessageViewModel,
chipbarCoordinator,
keyguardStateController,
+ shadeInteractor
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b506a36..7e826b8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -128,7 +128,6 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.EventLogTags;
-import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -146,6 +145,7 @@
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -181,7 +181,6 @@
import java.util.function.Consumer;
import dagger.Lazy;
-
import kotlinx.coroutines.CoroutineDispatcher;
/**
@@ -2833,7 +2832,7 @@
// playing in-window animations for this particular unlock since a previous unlock might
// have changed the Launcher state.
if (mWakeAndUnlocking
- && KeyguardUnlockAnimationController.Companion.isNexusLauncherUnderneath()) {
+ && mKeyguardUnlockAnimationControllerLazy.get().isNexusLauncherUnderneath()) {
flags |= KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
}
@@ -3287,7 +3286,7 @@
// of the in-window animations are reflected. This is needed even if we're not actually
// playing in-window animations for this particular unlock since a previous unlock might
// have changed the Launcher state.
- if (KeyguardUnlockAnimationController.Companion.isNexusLauncherUnderneath()) {
+ if (mKeyguardUnlockAnimationControllerLazy.get().isNexusLauncherUnderneath()) {
flags |= KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 2557e81..36b93cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -47,7 +47,6 @@
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -97,9 +96,6 @@
*/
val isKeyguardShowing: Flow<Boolean>
- /** Is the keyguard in a unlocked state? */
- val isKeyguardUnlocked: StateFlow<Boolean>
-
/** Is an activity showing over the keyguard? */
val isKeyguardOccluded: Flow<Boolean>
@@ -206,14 +202,6 @@
*/
fun isKeyguardShowing(): Boolean
- /**
- * Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
- * dismissed once the authentication challenge is completed. For example, completing a biometric
- * authentication challenge via face unlock or fingerprint sensor can automatically bypass the
- * lock screen.
- */
- fun isBypassEnabled(): Boolean
-
/** Sets whether the bottom area UI should animate the transition out of doze state. */
fun setAnimateDozingTransitions(animate: Boolean)
@@ -265,7 +253,6 @@
screenLifecycle: ScreenLifecycle,
biometricUnlockController: BiometricUnlockController,
private val keyguardStateController: KeyguardStateController,
- private val keyguardBypassController: KeyguardBypassController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val dozeTransitionListener: DozeTransitionListener,
private val dozeParameters: DozeParameters,
@@ -370,44 +357,6 @@
}
.distinctUntilChanged()
- override val isKeyguardUnlocked: StateFlow<Boolean> =
- conflatedCallbackFlow {
- val callback =
- object : KeyguardStateController.Callback {
- override fun onUnlockedChanged() {
- trySendWithFailureLogging(
- keyguardStateController.isUnlocked,
- TAG,
- "updated isKeyguardUnlocked due to onUnlockedChanged"
- )
- }
-
- override fun onKeyguardShowingChanged() {
- trySendWithFailureLogging(
- keyguardStateController.isUnlocked,
- TAG,
- "updated isKeyguardUnlocked due to onKeyguardShowingChanged"
- )
- }
- }
-
- keyguardStateController.addCallback(callback)
- // Adding the callback does not send an initial update.
- trySendWithFailureLogging(
- keyguardStateController.isUnlocked,
- TAG,
- "initial isKeyguardUnlocked"
- )
-
- awaitClose { keyguardStateController.removeCallback(callback) }
- }
- .distinctUntilChanged()
- .stateIn(
- scope,
- SharingStarted.Eagerly,
- initialValue = false,
- )
-
override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow {
val callback =
object : KeyguardStateController.Callback {
@@ -543,10 +492,6 @@
return keyguardStateController.isShowing
}
- override fun isBypassEnabled(): Boolean {
- return keyguardBypassController.bypassEnabled
- }
-
// TODO(b/297345631): Expose this at the interactor level instead so that it can be powered by
// [SceneInteractor] when scenes are ready.
override val statusBarState: StateFlow<StatusBarState> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 338f994..8063468 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -23,7 +23,6 @@
import android.graphics.Point
import android.util.MathUtils
import com.android.app.animation.Interpolators
-import com.android.systemui.res.R
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -31,6 +30,7 @@
import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.KeyguardRepository
@@ -40,10 +40,10 @@
import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState
-import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.ScreenModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.SceneKey
@@ -79,6 +79,7 @@
private val commandQueue: CommandQueue,
featureFlags: FeatureFlags,
sceneContainerFlags: SceneContainerFlags,
+ deviceEntryRepository: DeviceEntryRepository,
bouncerRepository: KeyguardBouncerRepository,
configurationRepository: ConfigurationRepository,
shadeRepository: ShadeRepository,
@@ -168,7 +169,7 @@
val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
/** Whether the keyguard is unlocked or not. */
- val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked
+ val isKeyguardUnlocked: Flow<Boolean> = deviceEntryRepository.isUnlocked
/** Whether the keyguard is occluded (covered by an activity). */
val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
@@ -323,26 +324,7 @@
repository.setAnimateDozingTransitions(animate)
}
- fun isKeyguardDismissable(): Boolean {
- return repository.isKeyguardUnlocked.value
- }
-
companion object {
private const val TAG = "KeyguardInteractor"
-
- fun isKeyguardVisibleInState(state: KeyguardState): Boolean {
- return when (state) {
- KeyguardState.OFF -> true
- KeyguardState.DOZING -> true
- KeyguardState.DREAMING -> true
- KeyguardState.AOD -> true
- KeyguardState.ALTERNATE_BOUNCER -> true
- KeyguardState.PRIMARY_BOUNCER -> true
- KeyguardState.LOCKSCREEN -> true
- KeyguardState.GONE -> false
- KeyguardState.OCCLUDED -> true
- KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> false
- }
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index f564d00..053727a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -32,6 +32,7 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.ViewPriority
@@ -55,6 +56,7 @@
occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
chipbarCoordinator: ChipbarCoordinator,
keyguardStateController: KeyguardStateController,
+ shadeInteractor: ShadeInteractor,
): DisposableHandle {
val disposableHandle =
view.repeatWhenAttached {
@@ -88,6 +90,17 @@
}
}
}
+
+ launch {
+ shadeInteractor.isAnyFullyExpanded.collect { isFullyAnyExpanded ->
+ view.visibility =
+ if (isFullyAnyExpanded) {
+ View.INVISIBLE
+ } else {
+ View.VISIBLE
+ }
+ }
+ }
}
repeatOnLifecycle(Lifecycle.State.STARTED) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 2ad74fb..864e345 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -62,6 +62,7 @@
import com.android.systemui.monet.ColorScheme
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.shared.clocks.DefaultClockController
import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants
@@ -109,6 +110,7 @@
private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
private val chipbarCoordinator: ChipbarCoordinator,
private val keyguardStateController: KeyguardStateController,
+ private val shadeInteractor: ShadeInteractor,
) {
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
@@ -317,6 +319,7 @@
occludingAppDeviceEntryMessageViewModel,
chipbarCoordinator,
keyguardStateController,
+ shadeInteractor,
)
)
rootView.addView(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
index 9409036..f4bc713 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
@@ -29,11 +29,11 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconView
import com.android.keyguard.LockIconViewController
-import com.android.systemui.res.R
import com.android.systemui.biometrics.AuthController
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import javax.inject.Inject
@@ -73,11 +73,11 @@
val mBottomPaddingPx =
context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
val bounds = windowManager.currentWindowMetrics.bounds
- val insets = windowManager.currentWindowMetrics.windowInsets
var widthPixels = bounds.right.toFloat()
if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
// Assumed to be initially neglected as there are no left or right insets in portrait.
// However, on landscape, these insets need to included when calculating the midpoint.
+ val insets = windowManager.currentWindowMetrics.windowInsets
widthPixels -= (insets.systemWindowInsetLeft + insets.systemWindowInsetRight).toFloat()
}
val heightPixels = bounds.bottom.toFloat()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 91b3357..c03e4d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -16,10 +16,10 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -34,26 +34,22 @@
@Inject
constructor(
@Application applicationScope: CoroutineScope,
- authenticationInteractor: AuthenticationInteractor,
+ deviceEntryInteractor: DeviceEntryInteractor,
communalInteractor: CommunalInteractor,
val longPress: KeyguardLongPressViewModel,
) {
/** The key of the scene we should switch to when swiping up. */
val upDestinationSceneKey: StateFlow<SceneKey> =
- authenticationInteractor.isUnlocked
+ deviceEntryInteractor.isUnlocked
.map { isUnlocked -> upDestinationSceneKey(isUnlocked) }
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = upDestinationSceneKey(authenticationInteractor.isUnlocked.value),
+ initialValue = upDestinationSceneKey(deviceEntryInteractor.isUnlocked.value),
)
private fun upDestinationSceneKey(isUnlocked: Boolean): SceneKey {
- return if (isUnlocked) {
- SceneKey.Gone
- } else {
- SceneKey.Bouncer
- }
+ return if (isUnlocked) SceneKey.Gone else SceneKey.Bouncer
}
/** The key of the scene we should switch to when swiping left. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
index ae3c912..ed6d41e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
@@ -101,7 +101,8 @@
panelEventsEvents: ShadeStateEvents,
private val secureSettings: SecureSettings,
@Main private val handler: Handler,
- private val splitShadeStateController: SplitShadeStateController
+ private val splitShadeStateController: SplitShadeStateController,
+ private val logger: MediaViewLogger,
) {
/** Track the media player setting status on lock screen. */
@@ -1057,6 +1058,7 @@
// that and directly set the mediaFrame's bounds within the premeasured host.
targetHost.addView(mediaFrame)
}
+ logger.logMediaHostAttachment(currentAttachmentLocation)
if (isCrossFadeAnimatorRunning) {
// When cross-fading with an animation, we only notify the media carousel of the
// location change, once the view is reattached to the new place and not
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
index 8f1595d..3ff23159 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
@@ -52,4 +52,8 @@
{ "location ($str1): $int1 -> $int2" }
)
}
+
+ fun logMediaHostAttachment(host: Int) {
+ buffer.log(TAG, LogLevel.DEBUG, { int1 = host }, { "Host (updateHostAttachment): $int1" })
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt
similarity index 80%
rename from packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt
index fbf9294..11d0be5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt
@@ -14,20 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.media
+package com.android.systemui.mediaprojection
import android.os.IBinder
import android.os.Parcel
import android.os.Parcelable
/**
- * Class that represents an area that should be captured.
- * Currently it has only a launch cookie that represents a task but
- * we potentially could add more identifiers e.g. for a pair of tasks.
+ * Class that represents an area that should be captured. Currently it has only a launch cookie that
+ * represents a task but we potentially could add more identifiers e.g. for a pair of tasks.
*/
-data class MediaProjectionCaptureTarget(
- val launchCookie: IBinder?
-): Parcelable {
+data class MediaProjectionCaptureTarget(val launchCookie: IBinder?) : Parcelable {
constructor(parcel: Parcel) : this(parcel.readStrongBinder())
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionServiceHelper.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/MediaProjectionServiceHelper.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
index 9e616e2..f1cade7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionServiceHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media
+package com.android.systemui.mediaprojection
import android.content.Context
import android.media.projection.IMediaProjection
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
index 88bc064..b5d3e91 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.media
+package com.android.systemui.mediaprojection.appselector
import android.app.ActivityOptions
import android.content.Intent
@@ -46,13 +46,11 @@
import com.android.internal.widget.RecyclerView
import com.android.internal.widget.RecyclerViewAccessibilityDelegate
import com.android.internal.widget.ResolverDrawerLayout
-import com.android.systemui.res.R
-import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorComponent
-import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorController
-import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
-import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorView
+import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
+import com.android.systemui.mediaprojection.MediaProjectionServiceHelper
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.AsyncActivityLauncher
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 33d9cc3..72aea04 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -23,8 +23,6 @@
import androidx.lifecycle.DefaultLifecycleObserver
import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.media.MediaProjectionAppSelectorActivity
-import com.android.systemui.media.MediaProjectionPermissionActivity
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
@@ -37,6 +35,7 @@
import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider
import com.android.systemui.mediaprojection.devicepolicy.MediaProjectionDevicePolicyModule
import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile
+import com.android.systemui.mediaprojection.permission.MediaProjectionPermissionActivity
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
import com.android.systemui.statusbar.policy.ConfigurationController
import dagger.Binds
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
index 64006fe..8b437c3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenrecord
+package com.android.systemui.mediaprojection.permission
import android.content.Context
import android.os.Bundle
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 4de6278..2b56d0c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media;
+package com.android.systemui.mediaprojection.permission;
import static android.media.projection.IMediaProjectionManager.EXTRA_PACKAGE_REUSING_GRANTED_CONSENT;
import static android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW_GRANTED_CONSENT;
@@ -22,8 +22,8 @@
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-import static com.android.systemui.screenrecord.ScreenShareOptionKt.ENTIRE_SCREEN;
-import static com.android.systemui.screenrecord.ScreenShareOptionKt.SINGLE_APP;
+import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.ENTIRE_SCREEN;
+import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.SINGLE_APP;
import android.annotation.Nullable;
import android.app.Activity;
@@ -51,13 +51,13 @@
import android.util.Log;
import android.view.Window;
-import com.android.systemui.res.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.MediaProjectionServiceHelper;
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
-import com.android.systemui.screenrecord.MediaProjectionPermissionDialog;
-import com.android.systemui.screenrecord.ScreenShareOption;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.Utils;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
index 47e28d8..2f10ad3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenrecord
+package com.android.systemui.mediaprojection.permission
import android.content.Context
import android.media.projection.MediaProjectionConfig
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
index ebf0dd2..37e8d9f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenrecord
+package com.android.systemui.mediaprojection.permission
import androidx.annotation.IntDef
import androidx.annotation.StringRes
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 9a9626d..10f95e0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -48,6 +48,7 @@
import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
+import com.android.systemui.qs.tiles.di.NewQSTileFactory;
import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
@@ -56,6 +57,8 @@
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.settings.SecureSettings;
+import dagger.Lazy;
+
import org.jetbrains.annotations.NotNull;
import java.io.PrintWriter;
@@ -121,6 +124,7 @@
@Inject
public QSTileHost(Context context,
+ Lazy<NewQSTileFactory> newQsTileFactoryProvider,
QSFactory defaultFactory,
@Main Executor mainExecutor,
PluginManager pluginManager,
@@ -147,6 +151,9 @@
mShadeController = shadeController;
+ if (featureFlags.getPipelineTilesEnabled()) {
+ mQsFactories.add(newQsTileFactoryProvider.get());
+ }
mQsFactories.add(defaultFactory);
pluginManager.addPluginListener(this, QSFactory.class, true);
mUserTracker = userTracker;
@@ -326,7 +333,6 @@
try {
tile = createTile(tileSpec);
if (tile != null) {
- tile.setTileSpec(tileSpec);
if (tile.isAvailable()) {
newTiles.put(tileSpec, tile);
mQSLogger.logTileAdded(tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index a6226b3..2af7ae0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -130,11 +130,9 @@
if (tile == null) {
continue;
} else if (!tile.isAvailable()) {
- tile.setTileSpec(spec);
tile.destroy();
continue;
}
- tile.setTileSpec(spec);
tilesToAdd.add(tile);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 92490e8..a65967a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -31,6 +31,7 @@
import com.android.systemui.qs.external.QSExternalModule;
import com.android.systemui.qs.pipeline.dagger.QSPipelineModule;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.policy.CastController;
@@ -41,14 +42,14 @@
import com.android.systemui.statusbar.policy.WalletController;
import com.android.systemui.util.settings.SecureSettings;
-import java.util.Map;
-
-import javax.inject.Named;
-
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.Multibinds;
+import java.util.Map;
+
+import javax.inject.Named;
+
/**
* Module for QS dependencies
*/
@@ -68,6 +69,11 @@
@Multibinds
Map<String, QSTileImpl<?>> tileMap();
+ /** A map of internal QS tile ViewModels. Ensures that this can be injected even if
+ * it is empty */
+ @Multibinds
+ Map<String, QSTileViewModel> tileViewModelMap();
+
@Provides
@SysUISingleton
static AutoTileManager provideAutoTileManager(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 34d6233..128c237 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -17,6 +17,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
+import android.app.IUriGrantsManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -31,6 +32,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Process;
import android.os.RemoteException;
import android.provider.Settings;
import android.service.quicksettings.IQSTileService;
@@ -43,11 +45,9 @@
import android.view.WindowManagerGlobal;
import android.widget.Switch;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -67,9 +67,10 @@
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
-import javax.inject.Inject;
-
import dagger.Lazy;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
public class CustomTile extends QSTileImpl<State> implements TileChangeListener {
public static final String PREFIX = "custom(";
@@ -109,24 +110,30 @@
private final AtomicBoolean mInitialDefaultIconFetched = new AtomicBoolean(false);
private final TileServices mTileServices;
- private CustomTile(
- QSHost host,
+ private int mServiceUid = Process.INVALID_UID;
+
+ private final IUriGrantsManager mIUriGrantsManager;
+
+ @AssistedInject
+ CustomTile(
+ Lazy<QSHost> host,
QsEventLogger uiEventLogger,
- Looper backgroundLooper,
- Handler mainHandler,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- String action,
- Context userContext,
+ @Assisted String action,
+ @Assisted Context userContext,
CustomTileStatePersister customTileStatePersister,
TileServices tileServices,
- DisplayTracker displayTracker
+ DisplayTracker displayTracker,
+ IUriGrantsManager uriGrantsManager
) {
- super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
+ super(host.get(), uiEventLogger, backgroundLooper, mainHandler, falsingManager,
+ metricsLogger, statusBarStateController, activityStarter, qsLogger);
mTileServices = tileServices;
mWindowManager = WindowManagerGlobal.getWindowManagerService();
mComponent = ComponentName.unflattenFromString(action);
@@ -139,6 +146,7 @@
mService = mServiceManager.getTileService();
mCustomTileStatePersister = customTileStatePersister;
mDisplayTracker = displayTracker;
+ mIUriGrantsManager = uriGrantsManager;
}
@Override
@@ -268,7 +276,8 @@
*
* @param tile tile populated with state to apply
*/
- public void updateTileState(Tile tile) {
+ public void updateTileState(Tile tile, int appUid) {
+ mServiceUid = appUid;
// This comes from a binder call IQSService.updateQsTile
mHandler.post(() -> handleUpdateTileState(tile));
}
@@ -433,14 +442,25 @@
state.state = tileState;
Drawable drawable = null;
try {
- drawable = mTile.getIcon().loadDrawable(mUserContext);
+ drawable = mTile.getIcon().loadDrawableCheckingUriGrant(
+ mUserContext,
+ mIUriGrantsManager,
+ mServiceUid,
+ mComponent.getPackageName()
+ );
} catch (Exception e) {
Log.w(TAG, "Invalid icon, forcing into unavailable state");
state.state = Tile.STATE_UNAVAILABLE;
- drawable = mDefaultIcon.loadDrawable(mUserContext);
}
- final Drawable drawableF = drawable;
+ final Drawable drawableF;
+ if (drawable != null) {
+ drawableF = drawable;
+ } else if (mDefaultIcon != null) {
+ drawableF = mDefaultIcon.loadDrawable(mUserContext);
+ } else {
+ drawableF = null;
+ }
state.iconSupplier = () -> {
if (drawableF == null) return null;
Drawable.ConstantState cs = drawableF.getConstantState();
@@ -543,96 +563,17 @@
/**
* Create a {@link CustomTile} for a given spec and user.
*
- * @param builder including injected common dependencies.
+ * @param factory including injected common dependencies.
* @param spec as provided by {@link CustomTile#toSpec}
* @param userContext context for the user that is creating this tile.
* @return a new {@link CustomTile}
*/
- public static CustomTile create(Builder builder, String spec, Context userContext) {
- return builder
- .setSpec(spec)
- .setUserContext(userContext)
- .build();
+ public static CustomTile create(Factory factory, String spec, Context userContext) {
+ return factory.create(getAction(spec), userContext);
}
- public static class Builder {
- final Lazy<QSHost> mQSHostLazy;
- final QsEventLogger mUiEventLogger;
- final Looper mBackgroundLooper;
- final Handler mMainHandler;
- private final FalsingManager mFalsingManager;
- final MetricsLogger mMetricsLogger;
- final StatusBarStateController mStatusBarStateController;
- final ActivityStarter mActivityStarter;
- final QSLogger mQSLogger;
- final CustomTileStatePersister mCustomTileStatePersister;
- private TileServices mTileServices;
- final DisplayTracker mDisplayTracker;
-
- Context mUserContext;
- String mSpec = "";
-
- @Inject
- public Builder(
- Lazy<QSHost> hostLazy,
- QsEventLogger uiEventLogger,
- @Background Looper backgroundLooper,
- @Main Handler mainHandler,
- FalsingManager falsingManager,
- MetricsLogger metricsLogger,
- StatusBarStateController statusBarStateController,
- ActivityStarter activityStarter,
- QSLogger qsLogger,
- CustomTileStatePersister customTileStatePersister,
- TileServices tileServices,
- DisplayTracker displayTracker
- ) {
- mQSHostLazy = hostLazy;
- mUiEventLogger = uiEventLogger;
- mBackgroundLooper = backgroundLooper;
- mMainHandler = mainHandler;
- mFalsingManager = falsingManager;
- mMetricsLogger = metricsLogger;
- mStatusBarStateController = statusBarStateController;
- mActivityStarter = activityStarter;
- mQSLogger = qsLogger;
- mCustomTileStatePersister = customTileStatePersister;
- mTileServices = tileServices;
- mDisplayTracker = displayTracker;
- }
-
- Builder setSpec(@NonNull String spec) {
- mSpec = spec;
- return this;
- }
-
- Builder setUserContext(@NonNull Context userContext) {
- mUserContext = userContext;
- return this;
- }
-
- @VisibleForTesting
- public CustomTile build() {
- if (mUserContext == null) {
- throw new NullPointerException("UserContext cannot be null");
- }
- String action = getAction(mSpec);
- return new CustomTile(
- mQSHostLazy.get(),
- mUiEventLogger,
- mBackgroundLooper,
- mMainHandler,
- mFalsingManager,
- mMetricsLogger,
- mStatusBarStateController,
- mActivityStarter,
- mQSLogger,
- action,
- mUserContext,
- mCustomTileStatePersister,
- mTileServices,
- mDisplayTracker
- );
- }
+ @AssistedFactory
+ public interface Factory {
+ CustomTile create(String action, Context userContext);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
index 1659c3e..c3c587d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.external
+import android.app.IUriGrantsManager
import android.content.Context
import android.graphics.drawable.Icon
import android.view.ContextThemeWrapper
@@ -34,7 +35,7 @@
* Dialog to present to the user to ask for authorization to add a [TileService].
*/
class TileRequestDialog(
- context: Context
+ context: Context,
) : SystemUIDialog(context) {
companion object {
@@ -44,7 +45,7 @@
/**
* Set the data of the tile to add, to show the user.
*/
- fun setTileData(tileData: TileData) {
+ fun setTileData(tileData: TileData, iUriGrantsManager: IUriGrantsManager) {
val ll = (LayoutInflater
.from(context)
.inflate(R.layout.tile_service_request_dialog, null)
@@ -54,7 +55,7 @@
.getString(R.string.qs_tile_request_dialog_text, tileData.appName)
}
addView(
- createTileView(tileData),
+ createTileView(tileData, iUriGrantsManager),
context.resources.getDimensionPixelSize(
R.dimen.qs_tile_service_request_tile_width),
context.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size)
@@ -65,13 +66,21 @@
setView(ll, spacing, spacing, spacing, spacing / 2)
}
- private fun createTileView(tileData: TileData): QSTileView {
+ private fun createTileView(
+ tileData: TileData,
+ iUriGrantsManager: IUriGrantsManager,
+ ): QSTileView {
val themedContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
val tile = QSTileViewImpl(themedContext, true)
val state = QSTile.BooleanState().apply {
label = tileData.label
handlesLongClick = false
- icon = tileData.icon?.loadDrawable(context)?.let {
+ icon = tileData.icon?.loadDrawableCheckingUriGrant(
+ context,
+ iUriGrantsManager,
+ tileData.callingUid,
+ tileData.packageName,
+ )?.let {
QSTileImpl.DrawableIcon(it)
} ?: ResourceIcon.get(R.drawable.android)
contentDescription = label
@@ -93,8 +102,10 @@
* @property icon Icon for the tile.
*/
data class TileData(
+ val callingUid: Int,
val appName: CharSequence,
val label: CharSequence,
- val icon: Icon?
+ val icon: Icon?,
+ val packageName: String,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
index 899d0e2..08567af 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.external
import android.app.Dialog
+import android.app.IUriGrantsManager
import android.app.StatusBarManager
import android.content.ComponentName
import android.content.DialogInterface
@@ -42,12 +43,13 @@
/**
* Controller to interface between [TileRequestDialog] and [QSHost].
*/
-class TileServiceRequestController constructor(
- private val qsHost: QSHost,
- private val commandQueue: CommandQueue,
- private val commandRegistry: CommandRegistry,
- private val eventLogger: TileRequestDialogEventLogger,
- private val dialogCreator: () -> TileRequestDialog = { TileRequestDialog(qsHost.context) }
+class TileServiceRequestController(
+ private val qsHost: QSHost,
+ private val commandQueue: CommandQueue,
+ private val commandRegistry: CommandRegistry,
+ private val eventLogger: TileRequestDialogEventLogger,
+ private val iUriGrantsManager: IUriGrantsManager,
+ private val dialogCreator: () -> TileRequestDialog = { TileRequestDialog(qsHost.context) }
) {
companion object {
@@ -62,13 +64,14 @@
private val commandQueueCallback = object : CommandQueue.Callbacks {
override fun requestAddTile(
+ callingUid: Int,
componentName: ComponentName,
appName: CharSequence,
label: CharSequence,
icon: Icon,
callback: IAddTileResultCallback
) {
- requestTileAdd(componentName, appName, label, icon) {
+ requestTileAdd(callingUid, componentName, appName, label, icon) {
try {
callback.onTileRequest(it)
} catch (e: RemoteException) {
@@ -98,6 +101,7 @@
@VisibleForTesting
internal fun requestTileAdd(
+ callingUid: Int,
componentName: ComponentName,
appName: CharSequence,
label: CharSequence,
@@ -119,7 +123,13 @@
eventLogger.logUserResponse(response, packageName, instanceId)
callback.accept(response)
}
- val tileData = TileRequestDialog.TileData(appName, label, icon)
+ val tileData = TileRequestDialog.TileData(
+ callingUid,
+ appName,
+ label,
+ icon,
+ componentName.packageName,
+ )
createDialog(tileData, dialogResponse).also { dialog ->
dialogCanceller = {
if (packageName == it) {
@@ -143,7 +153,7 @@
}
}
return dialogCreator().apply {
- setTileData(tileData)
+ setTileData(tileData, iUriGrantsManager)
setShowForAllUsers(true)
setCanceledOnTouchOutside(true)
setOnCancelListener { responseHandler.accept(DISMISSED) }
@@ -168,7 +178,7 @@
Log.w(TAG, "Malformed componentName ${args[0]}")
return
}
- requestTileAdd(componentName, args[1], args[2], null) {
+ requestTileAdd(0, componentName, args[1], args[2], null) {
Log.d(TAG, "Response: $it")
}
}
@@ -192,14 +202,16 @@
@SysUISingleton
class Builder @Inject constructor(
private val commandQueue: CommandQueue,
- private val commandRegistry: CommandRegistry
+ private val commandRegistry: CommandRegistry,
+ private val iUriGrantsManager: IUriGrantsManager,
) {
fun create(qsHost: QSHost): TileServiceRequestController {
return TileServiceRequestController(
qsHost,
commandQueue,
commandRegistry,
- TileRequestDialogEventLogger()
+ TileRequestDialogEventLogger(),
+ iUriGrantsManager,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index c8691ac..fc24022 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -184,7 +184,7 @@
}
}
- private void verifyCaller(CustomTile tile) {
+ private int verifyCaller(CustomTile tile) {
try {
String packageName = tile.getComponent().getPackageName();
int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
@@ -192,6 +192,7 @@
if (Binder.getCallingUid() != uid) {
throw new SecurityException("Component outside caller's uid");
}
+ return uid;
} catch (PackageManager.NameNotFoundException e) {
throw new SecurityException(e);
}
@@ -228,7 +229,7 @@
public void updateQsTile(Tile tile, IBinder token) {
CustomTile customTile = getTileForToken(token);
if (customTile != null) {
- verifyCaller(customTile);
+ int uid = verifyCaller(customTile);
synchronized (mServices) {
final TileServiceManager tileServiceManager = mServices.get(customTile);
if (tileServiceManager == null || !tileServiceManager.isLifecycleStarted()) {
@@ -239,7 +240,7 @@
tileServiceManager.clearPendingBind();
tileServiceManager.setLastUpdate(System.currentTimeMillis());
}
- customTile.updateTileState(tile);
+ customTile.updateTileState(tile, uid);
customTile.refreshState();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
index 21aaa94..b50798e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
@@ -32,6 +32,8 @@
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractorImpl
import com.android.systemui.qs.pipeline.domain.startable.QSPipelineCoreStartable
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractorImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -61,6 +63,11 @@
): InstalledTilesComponentRepository
@Binds
+ abstract fun provideDisabledByPolicyInteractor(
+ impl: DisabledByPolicyInteractorImpl
+ ): DisabledByPolicyInteractor
+
+ @Binds
@IntoMap
@ClassKey(QSPipelineCoreStartable::class)
abstract fun provideCoreStartable(startable: QSPipelineCoreStartable): CoreStartable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 00c2358..c5512c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -41,10 +41,12 @@
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.qs.tiles.di.NewQSTileFactory
import com.android.systemui.qs.toProto
import com.android.systemui.settings.UserTracker
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.pairwise
+import dagger.Lazy
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -131,6 +133,7 @@
private val installedTilesComponentRepository: InstalledTilesComponentRepository,
private val userRepository: UserRepository,
private val customTileStatePersister: CustomTileStatePersister,
+ private val newQSTileFactory: Lazy<NewQSTileFactory>,
private val tileFactory: QSFactory,
private val customTileAddedRepository: CustomTileAddedRepository,
private val tileLifecycleManagerFactory: TileLifecycleManager.Factory,
@@ -139,7 +142,7 @@
@Background private val backgroundDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope,
private val logger: QSPipelineLogger,
- featureFlags: QSPipelineFlagsRepository,
+ private val featureFlags: QSPipelineFlagsRepository,
) : CurrentTilesInteractor {
private val _currentSpecsAndTiles: MutableStateFlow<List<TileModel>> =
@@ -333,12 +336,19 @@
}
private suspend fun createTile(spec: TileSpec): QSTile? {
- val tile = withContext(mainDispatcher) { tileFactory.createTile(spec.spec) }
+ val tile =
+ withContext(mainDispatcher) {
+ if (featureFlags.pipelineTilesEnabled) {
+ newQSTileFactory.get().createTile(spec.spec)
+ } else {
+ null
+ }
+ ?: tileFactory.createTile(spec.spec)
+ }
if (tile == null) {
logger.logTileNotFoundInFactory(spec)
return null
} else {
- tile.tileSpec = spec.spec
return if (!tile.isAvailable) {
logger.logTileDestroyed(
spec,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
index 551b0f4..1a71b71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
@@ -1,7 +1,7 @@
package com.android.systemui.qs.pipeline.shared
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import javax.inject.Inject
@@ -10,7 +10,7 @@
class QSPipelineFlagsRepository
@Inject
constructor(
- private val featureFlags: FeatureFlags,
+ private val featureFlags: FeatureFlagsClassic,
) {
/** @see Flags.QS_PIPELINE_NEW_HOST */
@@ -20,4 +20,8 @@
/** @see Flags.QS_PIPELINE_AUTO_ADD */
val pipelineAutoAddEnabled: Boolean
get() = pipelineHostEnabled && featureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)
+
+ /** @see Flags.QS_PIPELINE_NEW_TILES */
+ val pipelineTilesEnabled: Boolean
+ get() = featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_TILES)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
index 11b5dd7..aed08f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
@@ -31,11 +31,7 @@
sealed class TileSpec private constructor(open val spec: String) {
/** Represents a spec that couldn't be parsed into a valid type of tile. */
- object Invalid : TileSpec("") {
- override fun toString(): String {
- return "TileSpec.INVALID"
- }
- }
+ data object Invalid : TileSpec("")
/** Container for the spec of a tile provided by SystemUI. */
data class PlatformTileSpec
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 9c7a734..38bbce4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -51,15 +51,15 @@
protected final Map<String, Provider<QSTileImpl<?>>> mTileMap;
private final Lazy<QSHost> mQsHostLazy;
- private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
+ private final Provider<CustomTile.Factory> mCustomTileFactoryProvider;
@Inject
public QSFactoryImpl(
Lazy<QSHost> qsHostLazy,
- Provider<CustomTile.Builder> customTileBuilderProvider,
+ Provider<CustomTile.Factory> customTileFactoryProvider,
Map<String, Provider<QSTileImpl<?>>> tileMap) {
mQsHostLazy = qsHostLazy;
- mCustomTileBuilderProvider = customTileBuilderProvider;
+ mCustomTileFactoryProvider = customTileFactoryProvider;
mTileMap = tileMap;
}
@@ -70,6 +70,7 @@
if (tile != null) {
tile.initialize();
tile.postStale(); // Tile was just created, must be stale.
+ tile.setTileSpec(tileSpec);
}
return tile;
}
@@ -86,7 +87,7 @@
// Custom tiles
if (tileSpec.startsWith(CustomTile.PREFIX)) {
return CustomTile.create(
- mCustomTileBuilderProvider.get(), tileSpec, mQsHostLazy.get().getUserContext());
+ mCustomTileFactoryProvider.get(), tileSpec, mQsHostLazy.get().getUserContext());
}
// Broken tiles.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
index e9f907c..9d10072 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
@@ -1,11 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.qs.tiles.base.actions
import android.content.Intent
+import android.view.View
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import javax.inject.Inject
/**
@@ -17,9 +33,9 @@
@Inject
constructor(private val activityStarter: ActivityStarter) {
- fun handle(userAction: QSTileUserAction, intent: Intent) {
+ fun handle(view: View?, intent: Intent) {
val animationController: ActivityLaunchAnimator.Controller? =
- userAction.view?.let {
+ view?.let {
ActivityLaunchAnimator.Controller.fromView(
it,
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt
new file mode 100644
index 0000000..056f967
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt
@@ -0,0 +1,127 @@
+/*
+ * 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.qs.tiles.base.interactor
+
+import android.content.Context
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
+import com.android.settingslib.RestrictedLockUtils
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
+import com.android.settingslib.RestrictedLockUtilsInternal
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor.PolicyResult
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+/**
+ * Provides restrictions data for the tiles. This is used in
+ * [com.android.systemui.qs.tiles.base.viewmodel.BaseQSTileViewModel] to determine if the tile is
+ * disabled based on the [com.android.systemui.qs.tiles.viewmodel.QSTileConfig.policy].
+ */
+interface DisabledByPolicyInteractor {
+
+ /**
+ * Checks if the tile is restricted by the policy for a specific user. Pass the result to the
+ * [handlePolicyResult] to let the user know that the tile is disable by the admin.
+ */
+ suspend fun isDisabled(userId: Int, userRestriction: String?): PolicyResult
+
+ /**
+ * Returns true when [policyResult] is [PolicyResult.TileDisabled] and has been handled by this
+ * method. No further handling is required and the input event can be skipped at this point.
+ *
+ * Returns false when [policyResult] is [PolicyResult.TileEnabled] and this method has done
+ * nothing.
+ */
+ fun handlePolicyResult(policyResult: PolicyResult): Boolean
+
+ sealed interface PolicyResult {
+ /** Tile has no policy restrictions. */
+ data object TileEnabled : PolicyResult
+
+ /**
+ * Tile is disabled by policy. Pass this to [DisabledByPolicyInteractor.handlePolicyResult]
+ * to show the user info screen using
+ * [RestrictedLockUtils.getShowAdminSupportDetailsIntent].
+ */
+ data class TileDisabled(val admin: EnforcedAdmin) : PolicyResult
+ }
+}
+
+@SysUISingleton
+class DisabledByPolicyInteractorImpl
+@Inject
+constructor(
+ private val context: Context,
+ private val activityStarter: ActivityStarter,
+ private val restrictedLockProxy: RestrictedLockProxy,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) : DisabledByPolicyInteractor {
+
+ override suspend fun isDisabled(userId: Int, userRestriction: String?): PolicyResult =
+ withContext(backgroundDispatcher) {
+ val admin: EnforcedAdmin =
+ restrictedLockProxy.getEnforcedAdmin(userId, userRestriction)
+ ?: return@withContext PolicyResult.TileEnabled
+
+ return@withContext if (
+ !restrictedLockProxy.hasBaseUserRestriction(userId, userRestriction)
+ ) {
+ PolicyResult.TileDisabled(admin)
+ } else {
+ PolicyResult.TileEnabled
+ }
+ }
+
+ override fun handlePolicyResult(policyResult: PolicyResult): Boolean =
+ when (policyResult) {
+ is PolicyResult.TileEnabled -> false
+ is PolicyResult.TileDisabled -> {
+ val intent =
+ RestrictedLockUtils.getShowAdminSupportDetailsIntent(
+ context,
+ policyResult.admin
+ )
+ activityStarter.postStartActivityDismissingKeyguard(intent, 0)
+ true
+ }
+ }
+}
+
+/** Mockable proxy for [RestrictedLockUtilsInternal] static methods. */
+@VisibleForTesting
+class RestrictedLockProxy @Inject constructor(private val context: Context) {
+
+ @WorkerThread
+ fun hasBaseUserRestriction(userId: Int, userRestriction: String?): Boolean =
+ RestrictedLockUtilsInternal.hasBaseUserRestriction(
+ context,
+ userRestriction,
+ userId,
+ )
+
+ @WorkerThread
+ fun getEnforcedAdmin(userId: Int, userRestriction: String?): EnforcedAdmin? =
+ RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ context,
+ userRestriction,
+ userId,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt
index 1a03481..7a22e3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.qs.tiles.base.interactor
import com.android.systemui.qs.tiles.viewmodel.QSTileState
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataRequest.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataRequest.kt
index 8289704..0aa6b0b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataRequest.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.qs.tiles.base.interactor
data class QSTileDataRequest(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt
index d6c9705..2bc6643 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.qs.tiles.base.interactor
import androidx.annotation.WorkerThread
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
index 8569fc7..14fc639 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.qs.tiles.base.interactor
import android.annotation.WorkerThread
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/StateUpdateTrigger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/StateUpdateTrigger.kt
index ed7ec8e..ffe38dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/StateUpdateTrigger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/StateUpdateTrigger.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.qs.tiles.base.interactor
import com.android.systemui.qs.tiles.viewmodel.QSTileState
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
index c2a75fa..58a335e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
@@ -1,8 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.qs.tiles.base.viewmodel
import androidx.annotation.CallSuper
import androidx.annotation.VisibleForTesting
import com.android.internal.util.Preconditions
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
import com.android.systemui.qs.tiles.base.interactor.QSTileDataRequest
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
@@ -10,10 +28,13 @@
import com.android.systemui.qs.tiles.base.interactor.StateUpdateTrigger
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileLifecycle
+import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
import com.android.systemui.util.kotlin.sample
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
@@ -25,6 +46,7 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -37,40 +59,35 @@
* Provides a hassle-free way to implement new tiles according to current System UI architecture
* standards. THis ViewModel is cheap to instantiate and does nothing until it's moved to
* [QSTileLifecycle.ALIVE] state.
+ *
+ * Inject [BaseQSTileViewModel.Factory] to create a new instance of this class.
*/
-abstract class BaseQSTileViewModel<DATA_TYPE>
+class BaseQSTileViewModel<DATA_TYPE>
@VisibleForTesting
constructor(
- final override val config: QSTileConfig,
+ override val config: QSTileConfig,
private val userActionInteractor: QSTileUserActionInteractor<DATA_TYPE>,
private val tileDataInteractor: QSTileDataInteractor<DATA_TYPE>,
private val mapper: QSTileDataToStateMapper<DATA_TYPE>,
+ private val disabledByPolicyInteractor: DisabledByPolicyInteractor,
private val backgroundDispatcher: CoroutineDispatcher,
private val tileScope: CoroutineScope,
) : QSTileViewModel {
- /**
- * @param config contains all the static information (like TileSpec) about the tile.
- * @param userActionInteractor encapsulates user input processing logic. Use it to start
- * activities, show dialogs or otherwise update the tile state.
- * @param tileDataInteractor provides [DATA_TYPE] and its availability.
- * @param backgroundDispatcher is used to run the internal [DATA_TYPE] processing and call
- * interactors methods. This should likely to be @Background CoroutineDispatcher.
- * @param mapper maps [DATA_TYPE] to the [QSTileState] that is then displayed by the View layer.
- * It's called in [backgroundDispatcher], so it's safe to perform long running operations
- * there.
- */
+ @AssistedInject
constructor(
- config: QSTileConfig,
- userActionInteractor: QSTileUserActionInteractor<DATA_TYPE>,
- tileDataInteractor: QSTileDataInteractor<DATA_TYPE>,
- mapper: QSTileDataToStateMapper<DATA_TYPE>,
- backgroundDispatcher: CoroutineDispatcher,
+ @Assisted config: QSTileConfig,
+ @Assisted userActionInteractor: QSTileUserActionInteractor<DATA_TYPE>,
+ @Assisted tileDataInteractor: QSTileDataInteractor<DATA_TYPE>,
+ @Assisted mapper: QSTileDataToStateMapper<DATA_TYPE>,
+ disabledByPolicyInteractor: DisabledByPolicyInteractor,
+ @Background backgroundDispatcher: CoroutineDispatcher,
) : this(
config,
userActionInteractor,
tileDataInteractor,
mapper,
+ disabledByPolicyInteractor,
backgroundDispatcher,
CoroutineScope(SupervisorJob())
)
@@ -92,7 +109,7 @@
.stateIn(
tileScope,
SharingStarted.WhileSubscribed(),
- false,
+ true,
)
private var currentLifeState: QSTileLifecycle = QSTileLifecycle.DEAD
@@ -145,7 +162,7 @@
userIds
.flatMapLatest { userId ->
merge(
- userInputFlow(),
+ userInputFlow(userId),
forceUpdates.map { StateUpdateTrigger.ForceUpdate },
)
.onStart { emit(StateUpdateTrigger.InitialRequest) }
@@ -172,14 +189,41 @@
replay = 1, // we only care about the most recent value
)
- private fun userInputFlow(): Flow<StateUpdateTrigger> {
+ private fun userInputFlow(userId: Int): Flow<StateUpdateTrigger> {
data class StateWithData<T>(val state: QSTileState, val data: T)
+ return when (config.policy) {
+ is QSTilePolicy.NoRestrictions -> userInputs
+ is QSTilePolicy.Restricted ->
+ userInputs.filter {
+ val result =
+ disabledByPolicyInteractor.isDisabled(userId, config.policy.userRestriction)
+ !disabledByPolicyInteractor.handlePolicyResult(result)
+ }
// Skip the input until there is some data
- return userInputs.sample(
- state.combine(tileData) { state, data -> StateWithData(state, data) }
- ) { input, stateWithData ->
+ }.sample(state.combine(tileData) { state, data -> StateWithData(state, data) }) {
+ input,
+ stateWithData ->
StateUpdateTrigger.UserAction(input, stateWithData.state, stateWithData.data)
}
}
+
+ interface Factory<T> {
+
+ /**
+ * @param config contains all the static information (like TileSpec) about the tile.
+ * @param userActionInteractor encapsulates user input processing logic. Use it to start
+ * activities, show dialogs or otherwise update the tile state.
+ * @param tileDataInteractor provides [DATA_TYPE] and its availability.
+ * @param mapper maps [DATA_TYPE] to the [QSTileState] that is then displayed by the View
+ * layer. It's called in [backgroundDispatcher], so it's safe to perform long running
+ * operations there.
+ */
+ fun create(
+ config: QSTileConfig,
+ userActionInteractor: QSTileUserActionInteractor<T>,
+ tileDataInteractor: QSTileDataInteractor<T>,
+ mapper: QSTileDataToStateMapper<T>,
+ ): BaseQSTileViewModel<T>
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
new file mode 100644
index 0000000..d0809c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.qs.tiles.di
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.qs.QSFactory
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tiles.viewmodel.QSTileLifecycle
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModelAdapter
+import javax.inject.Inject
+import javax.inject.Provider
+
+// TODO(b/http://b/299909989): Rename the factory after rollout
+@SysUISingleton
+class NewQSTileFactory
+@Inject
+constructor(
+ private val adapterFactory: QSTileViewModelAdapter.Factory,
+ private val tileMap:
+ Map<String, @JvmSuppressWildcards Provider<@JvmSuppressWildcards QSTileViewModel>>,
+) : QSFactory {
+
+ override fun createTile(tileSpec: String): QSTile? =
+ tileMap[tileSpec]?.let {
+ val tile = it.get()
+ tile.onLifecycle(QSTileLifecycle.ALIVE)
+ adapterFactory.create(tile)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
index a5eaac1..1a6cf99 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
@@ -1,14 +1,44 @@
+/*
+ * 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.qs.tiles.viewmodel
-import android.graphics.drawable.Icon
+import androidx.annotation.StringRes
+import com.android.internal.logging.InstanceId
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.pipeline.shared.TileSpec
data class QSTileConfig(
val tileSpec: TileSpec,
val tileIcon: Icon,
- val tileLabel: CharSequence,
-// TODO(b/299908705): Fill necessary params
-/*
-val instanceId: InstanceId,
- */
+ @StringRes val tileLabelRes: Int,
+ val instanceId: InstanceId,
+ val policy: QSTilePolicy = QSTilePolicy.NoRestrictions,
)
+
+/** Represents policy restrictions that may be imposed on the tile. */
+sealed interface QSTilePolicy {
+ /** Tile has no policy restrictions */
+ data object NoRestrictions : QSTilePolicy
+
+ /**
+ * Tile might be disabled by policy. [userRestriction] is usually a constant from
+ * [android.os.UserManager] like [android.os.UserManager.DISALLOW_AIRPLANE_MODE].
+ * [com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor] is commonly used
+ * to resolve this and show user a message when needed.
+ */
+ data class Restricted(val userRestriction: String) : QSTilePolicy
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
index 1d5c1bc..6d7c576 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.qs.tiles.viewmodel
enum class QSTileLifecycle {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index 53f9edf..0ccde74 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -1,18 +1,112 @@
+/*
+ * 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.qs.tiles.viewmodel
-import android.graphics.drawable.Icon
+import android.service.quicksettings.Tile
+import com.android.systemui.common.shared.model.Icon
+/**
+ * Represents current a state of the tile to be displayed in on the view. Consider using
+ * [QSTileState.build] for better state creation experience and preset default values for certain
+ * fields.
+ *
+ * // TODO(b/http://b/299909989): Clean up legacy mappings after the transition
+ */
data class QSTileState(
- val icon: Icon,
+ val icon: () -> Icon,
val label: CharSequence,
-// TODO(b/299908705): Fill necessary params
-/*
- val subtitle: CharSequence = "",
- val activeState: ActivationState = Active,
- val enabledState: Enabled = Enabled,
- val loopIconAnimation: Boolean = false,
- val secondaryIcon: Icon? = null,
- val slashState: SlashState? = null,
- val supportedActions: Collection<UserAction> = listOf(Click), clicks should be a default action
-*/
-)
+ val activationState: ActivationState,
+ val secondaryLabel: CharSequence?,
+ val supportedActions: Set<UserAction>,
+ val contentDescription: CharSequence?,
+ val stateDescription: CharSequence?,
+ val sideViewIcon: SideViewIcon,
+ val enabledState: EnabledState,
+ val expandedAccessibilityClassName: String?,
+) {
+
+ companion object {
+
+ fun build(icon: () -> Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
+ Builder(icon, label).apply(build).build()
+
+ fun build(icon: Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
+ build({ icon }, label, build)
+ }
+
+ enum class ActivationState(val legacyState: Int) {
+ // An unavailable state indicates that for some reason this tile is not currently available
+ // to the user, and will have no click action. The tile's icon will be tinted differently to
+ // reflect this state.
+ UNAVAILABLE(Tile.STATE_UNAVAILABLE),
+ // This represents a tile that is currently active. (e.g. wifi is connected, bluetooth is
+ // on, cast is casting). This is the default state.
+ ACTIVE(Tile.STATE_ACTIVE),
+ // This represents a tile that is currently in a disabled state but is still interactable. A
+ // disabled state indicates that the tile is not currently active (e.g. wifi disconnected or
+ // bluetooth disabled), but is still interactable by the user to modify this state.
+ INACTIVE(Tile.STATE_INACTIVE),
+ }
+
+ /**
+ * Enabled tile behaves as usual where is disabled one is frozen and inactive in its current
+ * [ActivationState].
+ */
+ enum class EnabledState {
+ ENABLED,
+ DISABLED,
+ }
+
+ enum class UserAction {
+ CLICK,
+ LONG_CLICK,
+ }
+
+ sealed interface SideViewIcon {
+ data class Custom(val icon: Icon) : SideViewIcon
+ data object Chevron : SideViewIcon
+ data object None : SideViewIcon
+ }
+
+ class Builder(
+ var icon: () -> Icon,
+ var label: CharSequence,
+ ) {
+ var activationState: ActivationState = ActivationState.INACTIVE
+ var secondaryLabel: CharSequence? = null
+ var supportedActions: Set<UserAction> = setOf(UserAction.CLICK)
+ var contentDescription: CharSequence? = null
+ var stateDescription: CharSequence? = null
+ var sideViewIcon: SideViewIcon = SideViewIcon.None
+ var enabledState: EnabledState = EnabledState.ENABLED
+ var expandedAccessibilityClassName: String? = null
+
+ fun build(): QSTileState =
+ QSTileState(
+ icon,
+ label,
+ activationState,
+ secondaryLabel,
+ supportedActions,
+ contentDescription,
+ stateDescription,
+ sideViewIcon,
+ enabledState,
+ expandedAccessibilityClassName,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
index f1f8f01..a145042 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
@@ -1,13 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.qs.tiles.viewmodel
-import android.content.Context
import android.view.View
sealed interface QSTileUserAction {
- val context: Context
val view: View?
- class Click(override val context: Context, override val view: View?) : QSTileUserAction
- class LongClick(override val context: Context, override val view: View?) : QSTileUserAction
+ class Click(override val view: View?) : QSTileUserAction
+ class LongClick(override val view: View?) : QSTileUserAction
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
index d66d0a1..e5cb7ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.qs.tiles.viewmodel
import kotlinx.coroutines.flow.SharedFlow
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
new file mode 100644
index 0000000..f6299e3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -0,0 +1,248 @@
+/*
+ * 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.qs.tiles.viewmodel
+
+import android.content.Context
+import android.util.Log
+import android.view.View
+import androidx.annotation.GuardedBy
+import com.android.internal.logging.InstanceId
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon
+import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.flow.collectIndexed
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+// TODO(b/http://b/299909989): Use QSTileViewModel directly after the rollout
+class QSTileViewModelAdapter
+@AssistedInject
+constructor(
+ private val qsHost: QSHost,
+ @Assisted private val qsTileViewModel: QSTileViewModel,
+) : QSTile {
+
+ private val context
+ get() = qsHost.context
+
+ @GuardedBy("callbacks")
+ private val callbacks: MutableCollection<QSTile.Callback> = mutableSetOf()
+ @GuardedBy("listeningClients")
+ private val listeningClients: MutableCollection<Any> = mutableSetOf()
+
+ // Cancels the jobs when the adapter is no longer alive
+ private val adapterScope = CoroutineScope(SupervisorJob())
+ // Cancels the jobs when clients stop listening
+ private val listeningScope = CoroutineScope(SupervisorJob())
+
+ init {
+ adapterScope.launch {
+ qsTileViewModel.isAvailable.collectIndexed { index, isAvailable ->
+ if (!isAvailable) {
+ qsHost.removeTile(tileSpec)
+ }
+ // qsTileViewModel.isAvailable flow often starts with isAvailable == true. That's
+ // why we only allow isAvailable == true once and throw an exception afterwards.
+ if (index > 0 && isAvailable) {
+ // See com.android.systemui.qs.pipeline.domain.model.AutoAddable for additional
+ // guidance on how to auto add your tile
+ throw UnsupportedOperationException("Turning on tile is not supported now")
+ }
+ }
+ }
+
+ // QSTileHost doesn't call this when userId is initialized
+ userSwitch(qsHost.userId)
+
+ if (DEBUG) {
+ Log.d(TAG, "Using new tiles for: $tileSpec")
+ }
+ }
+
+ override fun isAvailable(): Boolean = qsTileViewModel.isAvailable.value
+
+ override fun setTileSpec(tileSpec: String?) {
+ throw UnsupportedOperationException("Tile spec is immutable in new tiles")
+ }
+
+ override fun refreshState() {
+ qsTileViewModel.forceUpdate()
+ }
+
+ override fun addCallback(callback: QSTile.Callback?) {
+ callback ?: return
+ synchronized(callbacks) { callbacks.add(callback) }
+ }
+
+ override fun removeCallback(callback: QSTile.Callback?) {
+ callback ?: return
+ synchronized(callbacks) { callbacks.remove(callback) }
+ }
+
+ override fun removeCallbacks() {
+ synchronized(callbacks) { callbacks.clear() }
+ }
+
+ override fun click(view: View?) {
+ if (isActionSupported(QSTileState.UserAction.CLICK)) {
+ qsTileViewModel.onActionPerformed(QSTileUserAction.Click(view))
+ }
+ }
+
+ override fun secondaryClick(view: View?) {
+ if (isActionSupported(QSTileState.UserAction.CLICK)) {
+ qsTileViewModel.onActionPerformed(QSTileUserAction.Click(view))
+ }
+ }
+
+ override fun longClick(view: View?) {
+ if (isActionSupported(QSTileState.UserAction.LONG_CLICK)) {
+ qsTileViewModel.onActionPerformed(QSTileUserAction.LongClick(view))
+ }
+ }
+
+ private fun isActionSupported(action: QSTileState.UserAction): Boolean =
+ qsTileViewModel.currentState?.supportedActions?.contains(action) == true
+
+ override fun userSwitch(currentUser: Int) {
+ qsTileViewModel.onUserIdChanged(currentUser)
+ }
+
+ @Deprecated(
+ "Not needed as {@link com.android.internal.logging.UiEvent} will use #getMetricsSpec",
+ replaceWith = ReplaceWith("getMetricsSpec"),
+ )
+ override fun getMetricsCategory(): Int = 0
+
+ override fun setListening(client: Any?, listening: Boolean) {
+ client ?: return
+ synchronized(listeningClients) {
+ if (listening) {
+ listeningClients.add(client)
+ if (listeningClients.size == 1) {
+ qsTileViewModel.state
+ .map { mapState(context, it, qsTileViewModel.config) }
+ .onEach { legacyState ->
+ synchronized(callbacks) {
+ callbacks.forEach { it.onStateChanged(legacyState) }
+ }
+ }
+ .launchIn(listeningScope)
+ }
+ } else {
+ listeningClients.remove(client)
+ if (listeningClients.isEmpty()) {
+ listeningScope.coroutineContext.cancelChildren()
+ }
+ }
+ }
+ }
+
+ override fun isListening(): Boolean =
+ synchronized(listeningClients) { listeningClients.isNotEmpty() }
+
+ override fun setDetailListening(show: Boolean) {
+ // do nothing like QSTileImpl
+ }
+
+ override fun destroy() {
+ adapterScope.cancel()
+ listeningScope.cancel()
+ qsTileViewModel.onLifecycle(QSTileLifecycle.DEAD)
+ }
+
+ override fun getState(): QSTile.State? =
+ qsTileViewModel.currentState?.let { mapState(context, it, qsTileViewModel.config) }
+
+ override fun getInstanceId(): InstanceId = qsTileViewModel.config.instanceId
+ override fun getTileLabel(): CharSequence =
+ context.getString(qsTileViewModel.config.tileLabelRes)
+ override fun getTileSpec(): String = qsTileViewModel.config.tileSpec.spec
+
+ private companion object {
+
+ const val DEBUG = false
+ const val TAG = "QSTileVMAdapter"
+
+ fun mapState(
+ context: Context,
+ viewModelState: QSTileState,
+ config: QSTileConfig
+ ): QSTile.State =
+ // we have to use QSTile.BooleanState to support different side icons
+ // which are bound to instanceof QSTile.BooleanState in QSTileView.
+ QSTile.BooleanState().apply {
+ spec = config.tileSpec.spec
+ label = viewModelState.label
+ // This value is synthetic and doesn't have any meaning
+ value = false
+
+ secondaryLabel = viewModelState.secondaryLabel
+ handlesLongClick =
+ viewModelState.supportedActions.contains(QSTileState.UserAction.LONG_CLICK)
+
+ iconSupplier = Supplier {
+ when (val stateIcon = viewModelState.icon()) {
+ is Icon.Loaded -> DrawableIcon(stateIcon.drawable)
+ is Icon.Resource -> ResourceIcon.get(stateIcon.res)
+ }
+ }
+ state = viewModelState.activationState.legacyState
+
+ contentDescription = viewModelState.contentDescription
+ stateDescription = viewModelState.stateDescription
+
+ disabledByPolicy = viewModelState.enabledState == QSTileState.EnabledState.DISABLED
+ expandedAccessibilityClassName = viewModelState.expandedAccessibilityClassName
+
+ when (viewModelState.sideViewIcon) {
+ is QSTileState.SideViewIcon.Custom -> {
+ sideViewCustomDrawable =
+ when (viewModelState.sideViewIcon.icon) {
+ is Icon.Loaded -> viewModelState.sideViewIcon.icon.drawable
+ is Icon.Resource ->
+ context.getDrawable(viewModelState.sideViewIcon.icon.res)
+ }
+ }
+ is QSTileState.SideViewIcon.Chevron -> {
+ forceExpandIcon = true
+ }
+ is QSTileState.SideViewIcon.None -> {
+ forceExpandIcon = false
+ }
+ }
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+
+ fun create(qsTileViewModel: QSTileViewModel): QSTileViewModelAdapter
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 722d366..a3499bd 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -26,6 +26,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.DisplayId
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.model.SysUiState
@@ -63,6 +64,7 @@
constructor(
@Application private val applicationScope: CoroutineScope,
private val sceneInteractor: SceneInteractor,
+ private val deviceEntryInteractor: DeviceEntryInteractor,
private val authenticationInteractor: AuthenticationInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val flags: SceneContainerFlags,
@@ -119,7 +121,7 @@
/** Switches between scenes based on ever-changing application state. */
private fun automaticallySwitchScenes() {
applicationScope.launch {
- authenticationInteractor.isUnlocked
+ deviceEntryInteractor.isUnlocked
.mapNotNull { isUnlocked ->
val renderedScenes =
when (val transitionState = sceneInteractor.transitionState.value) {
@@ -130,7 +132,6 @@
transitionState.toScene,
)
}
- val isBypassEnabled = authenticationInteractor.isBypassEnabled()
when {
isUnlocked ->
when {
@@ -141,7 +142,7 @@
// When the device becomes unlocked in Lockscreen, go to Gone if
// bypass is enabled.
renderedScenes.contains(SceneKey.Lockscreen) ->
- if (isBypassEnabled) {
+ if (deviceEntryInteractor.isBypassEnabled()) {
SceneKey.Gone to
"device unlocked in Lockscreen scene with bypass"
} else {
@@ -191,7 +192,7 @@
}
WakefulnessState.STARTING_TO_WAKE -> {
val authMethod = authenticationInteractor.getAuthenticationMethod()
- val isUnlocked = authenticationInteractor.isUnlocked.value
+ val isUnlocked = deviceEntryInteractor.isUnlocked.value
when {
authMethod == AuthenticationMethodModel.None -> {
switchToScene(
@@ -241,7 +242,7 @@
/** Collects and reports signals into the falsing system. */
private fun collectFalsingSignals() {
applicationScope.launch {
- authenticationInteractor.isLockscreenDismissed.collect { isLockscreenDismissed ->
+ deviceEntryInteractor.isDeviceEntered.collect { isLockscreenDismissed ->
if (isLockscreenDismissed) {
falsingCollector.onSuccessfulUnlock()
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 7cdb655..3501b6b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -41,10 +41,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.media.MediaProjectionCaptureTarget;
+import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
+import com.android.systemui.res.R;
import com.android.systemui.screenrecord.ScreenMediaRecorder.ScreenMediaRecorderListener;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index b80a01212..3aab3bf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -51,7 +51,7 @@
import android.view.Surface;
import android.view.WindowManager;
-import com.android.systemui.media.MediaProjectionCaptureTarget;
+import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
import java.io.Closeable;
import java.io.File;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index f0ce8a4..f2e94e9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -18,7 +18,7 @@
import static android.app.Activity.RESULT_OK;
-import static com.android.systemui.media.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET;
+import static com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET;
import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL;
import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC;
import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC_AND_INTERNAL;
@@ -41,8 +41,8 @@
import androidx.annotation.Nullable;
+import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
import com.android.systemui.res.R;
-import com.android.systemui.media.MediaProjectionCaptureTarget;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.phone.SystemUIDialog;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index b5b7043..a1d5d98 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -32,10 +32,14 @@
import android.widget.Spinner
import android.widget.Switch
import androidx.annotation.LayoutRes
-import com.android.systemui.res.R
-import com.android.systemui.media.MediaProjectionAppSelectorActivity
-import com.android.systemui.media.MediaProjectionCaptureTarget
+import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
+import com.android.systemui.mediaprojection.permission.BaseScreenSharePermissionDialog
+import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
+import com.android.systemui.mediaprojection.permission.SINGLE_APP
+import com.android.systemui.mediaprojection.permission.ScreenShareOption
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.settings.UserContextProvider
/** Dialog to select screen recording options */
@@ -58,6 +62,7 @@
private lateinit var tapsView: View
private lateinit var audioSwitch: Switch
private lateinit var options: Spinner
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setDialogTitle(R.string.screenrecord_permission_dialog_title)
@@ -177,6 +182,7 @@
)
private const val DELAY_MS: Long = 3000
private const val INTERVAL_MS: Long = 1000
+
private fun createOptionList(): List<ScreenShareOption> {
return listOf(
ScreenShareOption(
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index b715237..6fa592c 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -30,12 +30,13 @@
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.haptics.slider.SeekableSliderEventProducer;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.ViewController;
@@ -283,6 +284,7 @@
private final VibratorHelper mVibratorHelper;
private final SystemClock mSystemClock;
private final CoroutineDispatcher mMainDispatcher;
+ private final ActivityStarter mActivityStarter;
@Inject
public Factory(
@@ -291,14 +293,15 @@
VibratorHelper vibratorHelper,
SystemClock clock,
FeatureFlagsClassic featureFlags,
- @Main CoroutineDispatcher mainDispatcher
- ) {
+ @Main CoroutineDispatcher mainDispatcher,
+ ActivityStarter activityStarter) {
mFalsingManager = falsingManager;
mUiEventLogger = uiEventLogger;
mFeatureFlags = featureFlags;
mVibratorHelper = vibratorHelper;
mSystemClock = clock;
mMainDispatcher = mainDispatcher;
+ mActivityStarter = activityStarter;
}
/**
@@ -314,6 +317,8 @@
int layout = getLayout();
BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context)
.inflate(layout, viewRoot, false);
+ root.setActivityStarter(mActivityStarter);
+
BrightnessSliderHapticPlugin plugin;
if (mFeatureFlags.isEnabled(HAPTIC_BRIGHTNESS_SLIDER)) {
plugin = new BrightnessSliderHapticPluginImpl(
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index c885492..5ecf07f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -33,6 +33,7 @@
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
/**
@@ -41,6 +42,7 @@
*/
public class BrightnessSliderView extends FrameLayout {
+ private ActivityStarter mActivityStarter;
@NonNull
private ToggleSeekBar mSlider;
private DispatchTouchEventListener mListener;
@@ -57,6 +59,10 @@
super(context, attrs);
}
+ public void setActivityStarter(@NonNull ActivityStarter activityStarter) {
+ mActivityStarter = activityStarter;
+ }
+
// Inflated from quick_settings_brightness_dialog
@Override
protected void onFinishInflate() {
@@ -65,6 +71,7 @@
mSlider = requireViewById(R.id.slider);
mSlider.setAccessibilityLabel(getContentDescription().toString());
+ mSlider.setActivityStarter(mActivityStarter);
// Finds the progress drawable. Assumes brightness_progress_drawable.xml
try {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
index a5a0ae7..6ec10da 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
@@ -23,8 +23,9 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.SeekBar;
+import androidx.annotation.NonNull;
+
import com.android.settingslib.RestrictedLockUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.plugins.ActivityStarter;
public class ToggleSeekBar extends SeekBar {
@@ -32,6 +33,8 @@
private RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin = null;
+ private ActivityStarter mActivityStarter;
+
public ToggleSeekBar(Context context) {
super(context);
}
@@ -49,7 +52,7 @@
if (mEnforcedAdmin != null) {
Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
mContext, mEnforcedAdmin);
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(intent, 0);
+ mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
return true;
}
if (!isEnabled()) {
@@ -74,4 +77,8 @@
public void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
mEnforcedAdmin = admin;
}
+
+ public void setActivityStarter(@NonNull ActivityStarter activityStarter) {
+ mActivityStarter = activityStarter;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index d5934e6..1038c67 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -114,7 +114,6 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.biometrics.AuthController;
@@ -163,6 +162,7 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.res.R;
import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.shared.system.QuickStepContract;
@@ -393,7 +393,6 @@
private TrackingStartedListener mTrackingStartedListener;
private OpenCloseListener mOpenCloseListener;
private GestureRecorder mGestureRecorder;
- private boolean mPanelExpanded;
private boolean mKeyguardQsUserSwitchEnabled;
private boolean mKeyguardUserSwitcherEnabled;
@@ -1321,7 +1320,7 @@
// when we switch between split shade and regular shade we want to enforce setting qs to
// the default state: expanded for split shade and collapsed otherwise
- if (!isKeyguardShowing() && mPanelExpanded) {
+ if (!isKeyguardShowing() && isPanelExpanded()) {
mQsController.setExpanded(mSplitShadeEnabled);
}
if (isKeyguardShowing() && mQsController.getExpanded() && mSplitShadeEnabled) {
@@ -2630,10 +2629,9 @@
private void updatePanelExpanded() {
boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
- if (mPanelExpanded != isExpanded) {
- mPanelExpanded = isExpanded;
+ if (isPanelExpanded() != isExpanded) {
+ setExpandedOrAwaitingInputTransfer(isExpanded);
updateSystemUiStateFlags();
- mShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(mPanelExpanded);
mShadeExpansionStateManager.onShadeExpansionFullyChanged(isExpanded);
if (!isExpanded) {
mQsController.closeQsCustomizer();
@@ -2641,9 +2639,13 @@
}
}
+ private void setExpandedOrAwaitingInputTransfer(boolean expandedOrAwaitingInputTransfer) {
+ mShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(expandedOrAwaitingInputTransfer);
+ }
+
@Override
public boolean isPanelExpanded() {
- return mPanelExpanded;
+ return mShadeRepository.getLegacyExpandedOrAwaitingInputTransfer().getValue();
}
private int calculatePanelHeightShade() {
@@ -3392,7 +3394,7 @@
ipw.print("mMaxAllowedKeyguardNotifications=");
ipw.println(mMaxAllowedKeyguardNotifications);
ipw.print("mAnimateNextPositionUpdate="); ipw.println(mAnimateNextPositionUpdate);
- ipw.print("mPanelExpanded="); ipw.println(mPanelExpanded);
+ ipw.print("isPanelExpanded()="); ipw.println(isPanelExpanded());
ipw.print("mKeyguardQsUserSwitchEnabled="); ipw.println(mKeyguardQsUserSwitchEnabled);
ipw.print("mKeyguardUserSwitcherEnabled="); ipw.println(mKeyguardUserSwitcherEnabled);
ipw.print("mDozing="); ipw.println(mDozing);
@@ -3606,7 +3608,7 @@
+ isFullyExpanded() + " inQs=" + mQsController.getExpanded());
}
mSysUiState
- .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE, mPanelExpanded)
+ .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE, isPanelExpanded())
.setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
isFullyExpanded() && !mQsController.getExpanded())
.setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index c803e6f..6f5e41f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -22,6 +22,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.app.IActivityManager;
import android.content.Context;
@@ -48,7 +49,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
@@ -58,7 +58,9 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.res.R;
import com.android.systemui.scene.ui.view.WindowRootViewComponent;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -71,6 +73,8 @@
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
@@ -108,6 +112,7 @@
private final KeyguardBypassController mKeyguardBypassController;
private final Executor mBackgroundExecutor;
private final AuthController mAuthController;
+ private final Lazy<ShadeInteractor> mShadeInteractorLazy;
private ViewGroup mWindowRootView;
private LayoutParams mLp;
private boolean mHasTopUi;
@@ -151,6 +156,7 @@
ScreenOffAnimationController screenOffAnimationController,
AuthController authController,
ShadeExpansionStateManager shadeExpansionStateManager,
+ Lazy<ShadeInteractor> shadeInteractorLazy,
ShadeWindowLogger logger) {
mContext = context;
mWindowRootViewComponentFactory = windowRootViewComponentFactory;
@@ -171,12 +177,12 @@
mLastKeyguardRotationAllowed = mKeyguardStateController.isKeyguardScreenRotationAllowed();
mLockScreenDisplayTimeout = context.getResources()
.getInteger(R.integer.config_lockScreenDisplayTimeout);
+ mShadeInteractorLazy = shadeInteractorLazy;
((SysuiStatusBarStateController) statusBarStateController)
.addCallback(mStateListener,
SysuiStatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
configurationController.addCallback(this);
shadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged);
- shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
float desiredPreferredRefreshRate = context.getResources()
.getInteger(R.integer.config_keyguardRefreshRate);
@@ -224,9 +230,9 @@
}
@VisibleForTesting
- void onShadeExpansionFullyChanged(Boolean isExpanded) {
- if (mCurrentState.panelExpanded != isExpanded) {
- mCurrentState.panelExpanded = isExpanded;
+ void onShadeOrQsExpanded(Boolean isExpanded) {
+ if (mCurrentState.shadeOrQsExpanded != isExpanded) {
+ mCurrentState.shadeOrQsExpanded = isExpanded;
apply(mCurrentState);
}
}
@@ -289,6 +295,11 @@
public void fetchWindowRootView() {
WindowRootViewComponent component = mWindowRootViewComponentFactory.create();
mWindowRootView = component.getWindowRootView();
+ collectFlow(
+ mWindowRootView,
+ mShadeInteractorLazy.get().isAnyExpanded(),
+ this::onShadeOrQsExpanded
+ );
}
@Override
@@ -384,7 +395,7 @@
}
private void applyFocusableFlag(NotificationShadeWindowState state) {
- boolean panelFocusable = state.notificationShadeFocusable && state.panelExpanded;
+ boolean panelFocusable = state.notificationShadeFocusable && state.shadeOrQsExpanded;
if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput)
|| ENABLE_REMOTE_INPUT && state.remoteInputActive
// Make the panel focusable if we're doing the screen off animation, since the light
@@ -408,7 +419,7 @@
}
private void applyForceShowNavigationFlag(NotificationShadeWindowState state) {
- if (state.panelExpanded || state.bouncerShowing
+ if (state.shadeOrQsExpanded || state.bouncerShowing
|| ENABLE_REMOTE_INPUT && state.remoteInputActive) {
mLpChanged.forciblyShownTypes |= WindowInsets.Type.navigationBars();
} else {
@@ -544,7 +555,7 @@
state.keyguardOccluded,
state.keyguardNeedsInput,
state.panelVisible,
- state.panelExpanded,
+ state.shadeOrQsExpanded,
state.notificationShadeFocusable,
state.bouncerShowing,
state.keyguardFadingAway,
@@ -582,7 +593,7 @@
mCurrentState.keyguardGoingAway,
mCurrentState.bouncerShowing,
mCurrentState.dozing,
- mCurrentState.panelExpanded,
+ mCurrentState.shadeOrQsExpanded,
mCurrentState.dreaming);
}
}
@@ -833,7 +844,7 @@
*/
@Override
public boolean getPanelExpanded() {
- return mCurrentState.panelExpanded;
+ return mCurrentState.shadeOrQsExpanded;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
index e3010ca..fbe164a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
@@ -32,7 +32,7 @@
@JvmField var keyguardNeedsInput: Boolean = false,
@JvmField var panelVisible: Boolean = false,
/** shade panel is expanded (expansion fraction > 0) */
- @JvmField var panelExpanded: Boolean = false,
+ @JvmField var shadeOrQsExpanded: Boolean = false,
@JvmField var notificationShadeFocusable: Boolean = false,
@JvmField var bouncerShowing: Boolean = false,
@JvmField var keyguardFadingAway: Boolean = false,
@@ -70,7 +70,7 @@
keyguardOccluded.toString(),
keyguardNeedsInput.toString(),
panelVisible.toString(),
- panelExpanded.toString(),
+ shadeOrQsExpanded.toString(),
notificationShadeFocusable.toString(),
bouncerShowing.toString(),
keyguardFadingAway.toString(),
@@ -137,7 +137,7 @@
this.keyguardOccluded = keyguardOccluded
this.keyguardNeedsInput = keyguardNeedsInput
this.panelVisible = panelVisible
- this.panelExpanded = panelExpanded
+ this.shadeOrQsExpanded = panelExpanded
this.notificationShadeFocusable = notificationShadeFocusable
this.bouncerShowing = bouncerShowing
this.keyguardFadingAway = keyguardFadingAway
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 251cc16..ac8333a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -129,6 +129,9 @@
combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) }
.stateIn(scope, SharingStarted.Eagerly, 0f)
+ /** Whether either the shade or QS is fully expanded. */
+ val isAnyFullyExpanded: Flow<Boolean> = anyExpansion.map { it >= 1f }.distinctUntilChanged()
+
/** Whether either the shade or QS is expanding from a fully collapsed state. */
val isAnyExpanding: Flow<Boolean> =
anyExpansion
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index 068d5a5..9c5a201 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -16,10 +16,10 @@
package com.android.systemui.shade.ui.viewmodel
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -34,15 +34,15 @@
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- authenticationInteractor: AuthenticationInteractor,
+ deviceEntryInteractor: DeviceEntryInteractor,
private val bouncerInteractor: BouncerInteractor,
val shadeHeaderViewModel: ShadeHeaderViewModel,
) {
/** The key of the scene we should switch to when swiping up. */
val upDestinationSceneKey: StateFlow<SceneKey> =
combine(
- authenticationInteractor.isUnlocked,
- authenticationInteractor.canSwipeToDismiss,
+ deviceEntryInteractor.isUnlocked,
+ deviceEntryInteractor.canSwipeToEnter,
) { isUnlocked, canSwipeToDismiss ->
upDestinationSceneKey(
isUnlocked = isUnlocked,
@@ -54,8 +54,8 @@
started = SharingStarted.WhileSubscribed(),
initialValue =
upDestinationSceneKey(
- isUnlocked = authenticationInteractor.isUnlocked.value,
- canSwipeToDismiss = authenticationInteractor.canSwipeToDismiss.value,
+ isUnlocked = deviceEntryInteractor.isUnlocked.value,
+ canSwipeToDismiss = deviceEntryInteractor.canSwipeToEnter.value,
),
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 93bb435..664103f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -442,6 +442,7 @@
* @see IStatusBar#requestAddTile
*/
default void requestAddTile(
+ int callingUid,
@NonNull ComponentName componentName,
@NonNull CharSequence appName,
@NonNull CharSequence label,
@@ -1314,6 +1315,7 @@
@Override
public void requestAddTile(
+ int callingUid,
@NonNull ComponentName componentName,
@NonNull CharSequence appName,
@NonNull CharSequence label,
@@ -1326,6 +1328,7 @@
args.arg3 = label;
args.arg4 = icon;
args.arg5 = callback;
+ args.arg6 = callingUid;
mHandler.obtainMessage(MSG_TILE_SERVICE_REQUEST_ADD, args).sendToTarget();
}
@@ -1772,8 +1775,9 @@
CharSequence label = (CharSequence) args.arg3;
Icon icon = (Icon) args.arg4;
IAddTileResultCallback callback = (IAddTileResultCallback) args.arg5;
+ int callingUid = (int) args.arg6;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).requestAddTile(
+ mCallbacks.get(i).requestAddTile(callingUid,
componentName, appName, label, icon, callback);
}
args.recycle();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index d908243..93c55de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -24,8 +24,6 @@
String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
= "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
- boolean shouldAllowLockscreenRemoteInput();
-
/**
* @param userId user Id
* @return true if we re on a secure lock screen
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index ea5ca27..2147510 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -49,8 +49,6 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.recents.OverviewProxyService;
@@ -63,14 +61,14 @@
import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.settings.SecureSettings;
-import dagger.Lazy;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
+import dagger.Lazy;
+
/**
* Handles keeping track of the current user, profiles, and various things related to hiding
* contents, redacting notifications, and the lockscreen.
@@ -81,8 +79,6 @@
NotificationLockscreenUserManager,
StateListener {
private static final String TAG = "LockscreenUserManager";
- private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
-
private final DeviceProvisionedController mDeviceProvisionedController;
private final KeyguardStateController mKeyguardStateController;
private final SecureSettings mSecureSettings;
@@ -103,9 +99,7 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private final NotificationClickNotifier mClickNotifier;
private final Lazy<OverviewProxyService> mOverviewProxyServiceLazy;
- private final FeatureFlags mFeatureFlags;
private boolean mShowLockscreenNotifications;
- private boolean mAllowLockscreenRemoteInput;
private LockPatternUtils mLockPatternUtils;
protected KeyguardManager mKeyguardManager;
private int mState = StatusBarState.SHADE;
@@ -181,22 +175,7 @@
protected final UserTracker.Callback mUserChangedCallback =
new UserTracker.Callback() {
@Override
- public void onUserChanged(int newUser, @NonNull Context userContext) {
- if (!mFeatureFlags.isEnabled(
- Flags.LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE)) {
- handleUserChange(newUser);
- }
- }
-
- @Override
public void onUserChanging(int newUser, @NonNull Context userContext) {
- if (mFeatureFlags.isEnabled(
- Flags.LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE)) {
- handleUserChange(newUser);
- }
- }
-
- private void handleUserChange(int newUser) {
mCurrentUserId = newUser;
updateCurrentProfilesCache();
@@ -239,8 +218,7 @@
KeyguardStateController keyguardStateController,
SecureSettings secureSettings,
DumpManager dumpManager,
- LockPatternUtils lockPatternUtils,
- FeatureFlags featureFlags) {
+ LockPatternUtils lockPatternUtils) {
mContext = context;
mMainHandler = mainHandler;
mDevicePolicyManager = devicePolicyManager;
@@ -258,7 +236,6 @@
mDeviceProvisionedController = deviceProvisionedController;
mSecureSettings = secureSettings;
mKeyguardStateController = keyguardStateController;
- mFeatureFlags = featureFlags;
dumpManager.registerDumpable(this);
}
@@ -305,14 +282,6 @@
Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
mSettingsObserver);
- if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
- mContext.getContentResolver().registerContentObserver(
- mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
- false,
- mSettingsObserver,
- UserHandle.USER_ALL);
- }
-
mBroadcastDispatcher.registerReceiver(mAllUsersReceiver,
new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
null /* handler */, UserHandle.ALL);
@@ -343,10 +312,6 @@
return mShowLockscreenNotifications;
}
- public boolean shouldAllowLockscreenRemoteInput() {
- return mAllowLockscreenRemoteInput;
- }
-
public boolean isCurrentProfile(int userId) {
synchronized (mLock) {
return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
@@ -357,10 +322,6 @@
mShowLockscreenNotifications = show;
}
- private void setLockscreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
- mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
- }
-
protected void updateLockscreenNotificationSetting() {
final boolean show = mSecureSettings.getIntForUser(
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
@@ -372,19 +333,6 @@
& DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
setShowLockscreenNotifications(show && allowedByDpm);
-
- if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
- final boolean remoteInput = mSecureSettings.getIntForUser(
- Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
- 0,
- mCurrentUserId) != 0;
- final boolean remoteInputDpm =
- (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
-
- setLockscreenAllowRemoteInput(remoteInput && remoteInputDpm);
- } else {
- setLockscreenAllowRemoteInput(false);
- }
}
/**
@@ -472,7 +420,6 @@
mUsersAllowingNotifications.append(userHandle, allowed);
return allowed;
}
-
return mUsersAllowingNotifications.get(userHandle);
}
@@ -639,37 +586,6 @@
}
}
-// public void updatePublicMode() {
-// //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns
-// // false when it should be true. Therefore, if we are not on the SHADE, don't even bother
-// // asking if the keyguard is showing. We still need to check it though because showing the
-// // camera on the keyguard has a state of SHADE but the keyguard is still showing.
-// final boolean showingKeyguard = mState != StatusBarState.SHADE
-// || mKeyguardStateController.isShowing();
-// final boolean devicePublic = showingKeyguard && isSecure(getCurrentUserId());
-//
-//
-// // Look for public mode users. Users are considered public in either case of:
-// // - device keyguard is shown in secure mode;
-// // - profile is locked with a work challenge.
-// SparseArray<UserInfo> currentProfiles = getCurrentProfiles();
-// for (int i = currentProfiles.size() - 1; i >= 0; i--) {
-// final int userId = currentProfiles.valueAt(i).id;
-// boolean isProfilePublic = devicePublic;
-// if (!devicePublic && userId != getCurrentUserId()) {
-// // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge
-// // due to a race condition where this code could be called before
-// // TrustManagerService updates its internal records, resulting in an incorrect
-// // state being cached in mLockscreenPublicMode. (b/35951989)
-// if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
-// && isSecure(userId)) {
-// isProfilePublic = mKeyguardManager.isDeviceLocked(userId);
-// }
-// }
-// setLockscreenPublicMode(isProfilePublic, userId);
-// }
-// }
-
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("NotificationLockscreenUserManager state:");
@@ -677,8 +593,6 @@
pw.println(mCurrentUserId);
pw.print(" mShowLockscreenNotifications=");
pw.println(mShowLockscreenNotifications);
- pw.print(" mAllowLockscreenRemoteInput=");
- pw.println(mAllowLockscreenRemoteInput);
pw.print(" mCurrentProfiles=");
synchronized (mLock) {
for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index befc64d..d4b6dfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -482,9 +482,6 @@
private boolean showBouncerForRemoteInput(View view, PendingIntent pendingIntent,
ExpandableNotificationRow row) {
- if (mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
- return false;
- }
final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 8baab25..f616b91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -244,12 +244,16 @@
}
final float scaledImageWidth = drawableWidth * scaleToFitIconView;
final float scaledImageHeight = drawableHeight * scaleToFitIconView;
- // if the scaled image size <= mOriginalStatusBarIconSize, we don't need to enlarge it
scaleToOriginalDrawingSize = Math.min(
(float) mOriginalStatusBarIconSize / scaledImageWidth,
(float) mOriginalStatusBarIconSize / scaledImageHeight);
if (scaleToOriginalDrawingSize > 1.0f) {
- scaleToOriginalDrawingSize = 1.0f;
+ // per b/296026932, if the scaled image size <= mOriginalStatusBarIconSize, we need
+ // to scale up the scaled image to fit in mOriginalStatusBarIconSize. But if both
+ // the raw drawable intrinsic width/height are less than mOriginalStatusBarIconSize,
+ // then we just scale up the scaled image back to the raw drawable size.
+ scaleToOriginalDrawingSize = Math.min(
+ scaleToOriginalDrawingSize, 1f / scaleToFitIconView);
}
}
iconScale = scaleToOriginalDrawingSize;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING b/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING
index 10e7573..718c1c0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING
@@ -4,9 +4,6 @@
"name": "CtsNotificationTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
@@ -14,9 +11,6 @@
},
{
"exclude-annotation": "androidx.test.filters.LargeTest"
- },
- {
- "exclude-annotation": "androidx.test.filters.LargeTest"
}
]
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index d089252..64dfc6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -167,11 +167,7 @@
* @see Vibrator#getPrimitiveDurations(int...)
*/
public int[] getPrimitiveDurations(int... primitiveIds) {
- if (!hasVibrator()) {
- return new int[]{0};
- } else {
- return mVibrator.getPrimitiveDurations(primitiveIds);
- }
+ return mVibrator.getPrimitiveDurations(primitiveIds);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index d1464ed..249c831 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -17,13 +17,16 @@
package com.android.systemui.statusbar.dagger
import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.core.StatusBarInitializer
import com.android.systemui.statusbar.data.repository.StatusBarModeRepository
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryImpl
+import com.android.systemui.statusbar.phone.LightBarController
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
/**
* A module for **only** classes related to the status bar **UI element**. This module specifically
@@ -49,4 +52,15 @@
@IntoMap
@ClassKey(OngoingCallController::class)
abstract fun bindOngoingCallController(impl: OngoingCallController): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(LightBarController::class)
+ abstract fun bindLightBarController(impl: LightBarController): CoreStartable
+
+ @Binds
+ @IntoSet
+ abstract fun statusBarInitializedListener(
+ statusBarModeRepository: StatusBarModeRepository,
+ ): StatusBarInitializer.OnStatusBarViewInitializedListener
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarAppearance.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarAppearance.kt
new file mode 100644
index 0000000..0cd31d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarAppearance.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.model
+
+import com.android.internal.view.AppearanceRegion
+import com.android.systemui.statusbar.phone.BoundsPair
+
+/** Keeps track of various parameters coordinating the appearance of the status bar. */
+data class StatusBarAppearance(
+ /** The current mode of the status bar. */
+ val mode: StatusBarMode,
+ /** The current bounds of the status bar. */
+ val bounds: BoundsPair,
+ /**
+ * A list of appearance regions for the appearance of the status bar background. Used to
+ * determine the correct coloring of status bar icons to ensure contrast. See
+ * [com.android.systemui.statusbar.phone.LightBarController].
+ */
+ val appearanceRegions: List<AppearanceRegion>,
+ /**
+ * The navigation bar color as set by
+ * [com.android.systemui.statusbar.CommandQueue.onSystemBarAttributesChanged].
+ *
+ * TODO(b/277764509): This likely belongs in a "NavigationBarAppearance"-type class, not a
+ * status bar class.
+ */
+ val navbarColorManagedByIme: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt
new file mode 100644
index 0000000..747efe3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.model
+
+import com.android.systemui.statusbar.phone.BarTransitions
+import com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT
+import com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT
+import com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE
+import com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT
+import com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT
+import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode
+
+/**
+ * The possible status bar modes.
+ *
+ * See the associated [BarTransitions] mode documentation for information about each of the modes
+ * and how they're used.
+ */
+enum class StatusBarMode {
+ /** Use a semi-transparent (aka translucent) background for the status bar. */
+ SEMI_TRANSPARENT,
+ /**
+ * A mode where notification icons in the status bar are hidden and replaced by a dot (this mode
+ * can be requested by apps). See
+ * [com.android.systemui.statusbar.phone.LightsOutNotifController].
+ */
+ LIGHTS_OUT,
+ /** Similar to [LIGHTS_OUT], but also with a transparent background for the status bar. */
+ LIGHTS_OUT_TRANSPARENT,
+ /** Use an opaque background for the status bar. */
+ OPAQUE,
+ /** Use a transparent background for the status bar. */
+ TRANSPARENT;
+
+ /** Converts a [StatusBarMode] to its [BarTransitions] integer. */
+ @TransitionMode
+ fun toTransitionModeInt(): Int {
+ return when (this) {
+ SEMI_TRANSPARENT -> MODE_SEMI_TRANSPARENT
+ LIGHTS_OUT -> MODE_LIGHTS_OUT
+ LIGHTS_OUT_TRANSPARENT -> MODE_LIGHTS_OUT_TRANSPARENT
+ OPAQUE -> MODE_OPAQUE
+ TRANSPARENT -> MODE_TRANSPARENT
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt
index 9d73071..2b05994 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt
@@ -16,8 +16,13 @@
package com.android.systemui.statusbar.data.repository
+import android.graphics.Rect
import android.view.WindowInsets
import android.view.WindowInsetsController
+import android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS
+import android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS
+import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS
+import android.view.WindowInsetsController.Appearance
import com.android.internal.statusbar.LetterboxDetails
import com.android.internal.view.AppearanceRegion
import com.android.systemui.CoreStartable
@@ -25,12 +30,22 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.DisplayId
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.core.StatusBarInitializer
+import com.android.systemui.statusbar.data.model.StatusBarAppearance
+import com.android.systemui.statusbar.data.model.StatusBarMode
+import com.android.systemui.statusbar.phone.BoundsPair
+import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
+import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
+import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
+import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -41,7 +56,7 @@
* Note: These status bar modes are status bar *window* states that are sent to us from
* WindowManager, not determined internally.
*/
-interface StatusBarModeRepository {
+interface StatusBarModeRepository : StatusBarInitializer.OnStatusBarViewInitializedListener {
/**
* True if the status bar window is showing transiently and will disappear soon, and false
* otherwise. ("Otherwise" in this case means the status bar is persistently hidden OR
@@ -62,6 +77,16 @@
val isInFullscreenMode: StateFlow<Boolean>
/**
+ * The current status bar appearance parameters.
+ *
+ * Null at system startup, but non-null once the first system callback has been received.
+ */
+ val statusBarAppearance: StateFlow<StatusBarAppearance?>
+
+ /** The current mode of the status bar. */
+ val statusBarMode: StateFlow<StatusBarMode>
+
+ /**
* Requests for the status bar to be shown transiently.
*
* TODO(b/277764509): Don't allow [CentralSurfaces] to set the transient mode; have it
@@ -85,6 +110,8 @@
@Application scope: CoroutineScope,
@DisplayId thisDisplayId: Int,
private val commandQueue: CommandQueue,
+ private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator,
+ ongoingCallRepository: OngoingCallRepository,
) : StatusBarModeRepository, CoreStartable {
private val commandQueueCallback =
@@ -114,7 +141,7 @@
override fun onSystemBarAttributesChanged(
displayId: Int,
- @WindowInsetsController.Appearance appearance: Int,
+ @Appearance appearance: Int,
appearanceRegions: Array<AppearanceRegion>,
navbarColorManagedByIme: Boolean,
@WindowInsetsController.Behavior behavior: Int,
@@ -125,7 +152,11 @@
if (displayId != thisDisplayId) return
_originalStatusBarAttributes.value =
StatusBarAttributes(
+ appearance,
+ appearanceRegions.toList(),
+ navbarColorManagedByIme,
requestedVisibleTypes,
+ letterboxDetails.toList(),
)
}
}
@@ -139,6 +170,19 @@
private val _originalStatusBarAttributes = MutableStateFlow<StatusBarAttributes?>(null)
+ private val _statusBarBounds = MutableStateFlow(BoundsPair(Rect(), Rect()))
+
+ override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) {
+ val statusBarBoundsProvider = component.boundsProvider
+ val listener =
+ object : StatusBarBoundsProvider.BoundsChangeListener {
+ override fun onStatusBarBoundsChanged(bounds: BoundsPair) {
+ _statusBarBounds.value = bounds
+ }
+ }
+ statusBarBoundsProvider.addChangeListener(listener)
+ }
+
override val isInFullscreenMode: StateFlow<Boolean> =
_originalStatusBarAttributes
.map { params ->
@@ -148,6 +192,89 @@
}
.stateIn(scope, SharingStarted.Eagerly, false)
+ /** Modifies the raw [StatusBarAttributes] if letterboxing is needed. */
+ private val modifiedStatusBarAttributes: StateFlow<ModifiedStatusBarAttributes?> =
+ combine(
+ _originalStatusBarAttributes,
+ _statusBarBounds,
+ ) { originalAttributes, statusBarBounds ->
+ if (originalAttributes == null) {
+ null
+ } else {
+ val (newAppearance, newAppearanceRegions) =
+ modifyAppearanceIfNeeded(
+ originalAttributes.appearance,
+ originalAttributes.appearanceRegions,
+ originalAttributes.letterboxDetails,
+ statusBarBounds,
+ )
+ ModifiedStatusBarAttributes(
+ newAppearance,
+ newAppearanceRegions,
+ originalAttributes.navbarColorManagedByIme,
+ statusBarBounds,
+ )
+ }
+ }
+ .stateIn(scope, SharingStarted.Eagerly, initialValue = null)
+
+ override val statusBarAppearance: StateFlow<StatusBarAppearance?> =
+ combine(
+ modifiedStatusBarAttributes,
+ isTransientShown,
+ isInFullscreenMode,
+ ongoingCallRepository.hasOngoingCall,
+ ) { modifiedAttributes, isTransientShown, isInFullscreenMode, hasOngoingCall ->
+ if (modifiedAttributes == null) {
+ null
+ } else {
+ val statusBarMode =
+ toBarMode(
+ modifiedAttributes.appearance,
+ isTransientShown,
+ isInFullscreenMode,
+ hasOngoingCall,
+ )
+ StatusBarAppearance(
+ statusBarMode,
+ modifiedAttributes.statusBarBounds,
+ modifiedAttributes.appearanceRegions,
+ modifiedAttributes.navbarColorManagedByIme,
+ )
+ }
+ }
+ .stateIn(scope, SharingStarted.Eagerly, initialValue = null)
+
+ override val statusBarMode: StateFlow<StatusBarMode> =
+ statusBarAppearance
+ .map { it?.mode ?: StatusBarMode.TRANSPARENT }
+ .stateIn(scope, SharingStarted.Eagerly, initialValue = StatusBarMode.TRANSPARENT)
+
+ private fun toBarMode(
+ appearance: Int,
+ isTransientShown: Boolean,
+ isInFullscreenMode: Boolean,
+ hasOngoingCall: Boolean,
+ ): StatusBarMode {
+ return when {
+ hasOngoingCall && isInFullscreenMode -> StatusBarMode.SEMI_TRANSPARENT
+ isTransientShown -> StatusBarMode.SEMI_TRANSPARENT
+ else -> appearance.toBarMode()
+ }
+ }
+
+ @Appearance
+ private fun Int.toBarMode(): StatusBarMode {
+ val lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS or APPEARANCE_OPAQUE_STATUS_BARS
+ return when {
+ this and lightsOutOpaque == lightsOutOpaque -> StatusBarMode.LIGHTS_OUT
+ this and APPEARANCE_LOW_PROFILE_BARS != 0 -> StatusBarMode.LIGHTS_OUT_TRANSPARENT
+ this and APPEARANCE_OPAQUE_STATUS_BARS != 0 -> StatusBarMode.OPAQUE
+ this and APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS != 0 -> StatusBarMode.SEMI_TRANSPARENT
+ else -> StatusBarMode.TRANSPARENT
+ }
+ }
+
override fun showTransient() {
_isTransientShown.value = true
}
@@ -156,11 +283,54 @@
_isTransientShown.value = false
}
+ private fun modifyAppearanceIfNeeded(
+ appearance: Int,
+ appearanceRegions: List<AppearanceRegion>,
+ letterboxDetails: List<LetterboxDetails>,
+ statusBarBounds: BoundsPair,
+ ): Pair<Int, List<AppearanceRegion>> =
+ if (shouldUseLetterboxAppearance(letterboxDetails)) {
+ val letterboxAppearance =
+ letterboxAppearanceCalculator.getLetterboxAppearance(
+ appearance,
+ appearanceRegions,
+ letterboxDetails,
+ statusBarBounds,
+ )
+ Pair(letterboxAppearance.appearance, letterboxAppearance.appearanceRegions)
+ } else {
+ Pair(appearance, appearanceRegions)
+ }
+
+ private fun shouldUseLetterboxAppearance(letterboxDetails: List<LetterboxDetails>) =
+ letterboxDetails.isNotEmpty()
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("originalStatusBarAttributes: ${_originalStatusBarAttributes.value}")
+ pw.println("modifiedStatusBarAttributes: ${modifiedStatusBarAttributes.value}")
+ pw.println("statusBarMode: ${statusBarMode.value}")
+ }
+
/**
* Internal class keeping track of the raw status bar attributes received from the callback.
* Should never be exposed.
*/
private data class StatusBarAttributes(
+ @Appearance val appearance: Int,
+ val appearanceRegions: List<AppearanceRegion>,
+ val navbarColorManagedByIme: Boolean,
@WindowInsets.Type.InsetsType val requestedVisibleTypes: Int,
+ val letterboxDetails: List<LetterboxDetails>,
+ )
+
+ /**
+ * Internal class keeping track of how [StatusBarAttributes] were transformed into new
+ * attributes based on letterboxing and other factors. Should never be exposed.
+ */
+ private data class ModifiedStatusBarAttributes(
+ @Appearance val appearance: Int,
+ val appearanceRegions: List<AppearanceRegion>,
+ val navbarColorManagedByIme: Boolean,
+ val statusBarBounds: BoundsPair,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt
new file mode 100644
index 0000000..4deebdb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.row
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import com.android.internal.widget.CallLayout
+import javax.inject.Inject
+
+class CallLayoutSetDataAsyncFactory @Inject constructor() : NotifRemoteViewsFactory {
+ override fun instantiate(
+ row: ExpandableNotificationRow,
+ @NotificationRowContentBinder.InflationFlag layoutType: Int,
+ parent: View?,
+ name: String,
+ context: Context,
+ attrs: AttributeSet
+ ): View? =
+ if (name == CallLayout::class.java.name)
+ CallLayout(context, attrs).apply { setSetDataAsyncEnabled(true) }
+ else null
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
index 0239afc..3a59978 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -61,7 +61,8 @@
static Set<NotifRemoteViewsFactory> provideNotifRemoteViewsFactories(
FeatureFlags featureFlags,
PrecomputedTextViewFactory precomputedTextViewFactory,
- BigPictureLayoutInflaterFactory bigPictureLayoutInflaterFactory
+ BigPictureLayoutInflaterFactory bigPictureLayoutInflaterFactory,
+ CallLayoutSetDataAsyncFactory callLayoutSetDataAsyncFactory
) {
final Set<NotifRemoteViewsFactory> replacementFactories = new HashSet<>();
if (featureFlags.isEnabled(Flags.PRECOMPUTED_TEXT)) {
@@ -70,6 +71,9 @@
if (featureFlags.isEnabled(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)) {
replacementFactories.add(bigPictureLayoutInflaterFactory);
}
+ if (featureFlags.isEnabled(Flags.CALL_LAYOUT_ASYNC_SET_DATA)) {
+ replacementFactories.add(callLayoutSetDataAsyncFactory);
+ }
return replacementFactories;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 875a409..91b12cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -20,6 +20,9 @@
import static com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.DEFAULT_HEADER_VISIBLE_AMOUNT;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
@@ -34,8 +37,7 @@
import android.widget.ProgressBar;
import android.widget.TextView;
-import androidx.annotation.Nullable;
-
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.NotificationActionListLayout;
import com.android.systemui.Dependency;
@@ -49,6 +51,8 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.HybridNotificationView;
+import java.util.function.Consumer;
+
/**
* Wraps a notification view inflated from a template.
*/
@@ -66,9 +70,13 @@
private int mContentHeight;
private int mMinHeightHint;
+ @Nullable
private NotificationActionListLayout mActions;
- private ArraySet<PendingIntent> mCancelledPendingIntents = new ArraySet<>();
- private UiOffloadThread mUiOffloadThread;
+ // Holds list of pending intents that have been cancelled by now - we only keep hash codes
+ // to avoid holding full binder proxies for intents that may have been removed by now.
+ @NonNull
+ @VisibleForTesting
+ final ArraySet<Integer> mCancelledPendingIntents = new ArraySet<>();
private View mRemoteInputHistory;
private boolean mCanHideHeader;
private float mHeaderTranslation;
@@ -147,6 +155,7 @@
com.android.internal.R.dimen.notification_content_margin_top);
}
+ @MainThread
private void resolveTemplateViews(StatusBarNotification sbn) {
mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon);
if (mRightIcon != null) {
@@ -195,34 +204,57 @@
return getLargeIcon(n);
}
+ @MainThread
private void updatePendingIntentCancellations() {
if (mActions != null) {
int numActions = mActions.getChildCount();
+ final ArraySet<Integer> currentlyActivePendingIntents = new ArraySet<>(numActions);
for (int i = 0; i < numActions; i++) {
Button action = (Button) mActions.getChildAt(i);
- performOnPendingIntentCancellation(action, () -> {
- if (action.isEnabled()) {
- action.setEnabled(false);
- // The visual appearance doesn't look disabled enough yet, let's add the
- // alpha as well. Since Alpha doesn't play nicely right now with the
- // transformation, we rather blend it manually with the background color.
- ColorStateList textColors = action.getTextColors();
- int[] colors = textColors.getColors();
- int[] newColors = new int[colors.length];
- float disabledAlpha = mView.getResources().getFloat(
- com.android.internal.R.dimen.notification_action_disabled_alpha);
- for (int j = 0; j < colors.length; j++) {
- int color = colors[j];
- color = blendColorWithBackground(color, disabledAlpha);
- newColors[j] = color;
- }
- ColorStateList newColorStateList = new ColorStateList(
- textColors.getStates(), newColors);
- action.setTextColor(newColorStateList);
+ PendingIntent pendingIntent = getPendingIntentForAction(action);
+ // Check if passed intent has already been cancelled in this class and immediately
+ // disable the action to avoid temporary race with enable/disable.
+ if (pendingIntent != null) {
+ int pendingIntentHashCode = getHashCodeForPendingIntent(pendingIntent);
+ currentlyActivePendingIntents.add(pendingIntentHashCode);
+ if (mCancelledPendingIntents.contains(pendingIntentHashCode)) {
+ disableActionView(action);
}
- });
+ }
+ updatePendingIntentCancellationListener(action, pendingIntent);
+ }
+
+ // This cleanup ensures that the size of this set doesn't grow into unreasonable sizes.
+ // There are scenarios where applications updated notifications with different
+ // PendingIntents which could cause this Set to grow to 1000+ elements.
+ mCancelledPendingIntents.retainAll(currentlyActivePendingIntents);
+ }
+ }
+
+ @MainThread
+ private void updatePendingIntentCancellationListener(Button action,
+ @Nullable PendingIntent pendingIntent) {
+ ActionPendingIntentCancellationHandler cancellationHandler = null;
+ if (pendingIntent != null) {
+ // Attach listeners to handle intent cancellation to this view.
+ cancellationHandler = new ActionPendingIntentCancellationHandler(pendingIntent, action,
+ this::disableActionViewWithIntent);
+ action.addOnAttachStateChangeListener(cancellationHandler);
+ // Immediately fire the event if the view is already attached to register
+ // pending intent cancellation listener.
+ if (action.isAttachedToWindow()) {
+ cancellationHandler.onViewAttachedToWindow(action);
}
}
+
+ // If the view has an old attached listener, remove it to avoid leaking intents.
+ ActionPendingIntentCancellationHandler previousHandler =
+ (ActionPendingIntentCancellationHandler) action.getTag(
+ R.id.pending_intent_listener_tag);
+ if (previousHandler != null) {
+ previousHandler.remove();
+ }
+ action.setTag(R.id.pending_intent_listener_tag, cancellationHandler);
}
private int blendColorWithBackground(int color, float alpha) {
@@ -231,42 +263,6 @@
Color.red(color), Color.green(color), Color.blue(color)), resolveBackgroundColor());
}
- private void performOnPendingIntentCancellation(View view, Runnable cancellationRunnable) {
- PendingIntent pendingIntent = (PendingIntent) view.getTag(
- com.android.internal.R.id.pending_intent_tag);
- if (pendingIntent == null) {
- return;
- }
- if (mCancelledPendingIntents.contains(pendingIntent)) {
- cancellationRunnable.run();
- } else {
- PendingIntent.CancelListener listener = (PendingIntent intent) -> {
- mView.post(() -> {
- mCancelledPendingIntents.add(pendingIntent);
- cancellationRunnable.run();
- });
- };
- if (mUiOffloadThread == null) {
- mUiOffloadThread = Dependency.get(UiOffloadThread.class);
- }
- if (view.isAttachedToWindow()) {
- mUiOffloadThread.execute(() -> pendingIntent.registerCancelListener(listener));
- }
- view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- mUiOffloadThread.execute(() -> pendingIntent.registerCancelListener(listener));
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- mUiOffloadThread.execute(
- () -> pendingIntent.unregisterCancelListener(listener));
- }
- });
- }
- }
-
@Override
public void onContentUpdated(ExpandableNotificationRow row) {
// Reinspect the notification. Before the super call, because the super call also updates
@@ -364,4 +360,141 @@
}
return extra + super.getExtraMeasureHeight();
}
+
+ /**
+ * This finds Action view with a given intent and disables it.
+ * With maximum of 3 views, this is sufficiently fast to iterate on main thread every time.
+ */
+ @MainThread
+ private void disableActionViewWithIntent(PendingIntent intent) {
+ mCancelledPendingIntents.add(getHashCodeForPendingIntent(intent));
+ if (mActions != null) {
+ int numActions = mActions.getChildCount();
+ for (int i = 0; i < numActions; i++) {
+ Button action = (Button) mActions.getChildAt(i);
+ PendingIntent pendingIntent = getPendingIntentForAction(action);
+ if (intent.equals(pendingIntent)) {
+ disableActionView(action);
+ }
+ }
+ }
+ }
+
+ /**
+ * Disables Action view when, e.g., its PendingIntent is disabled.
+ */
+ @MainThread
+ private void disableActionView(Button action) {
+ if (action.isEnabled()) {
+ action.setEnabled(false);
+ // The visual appearance doesn't look disabled enough yet, let's add the
+ // alpha as well. Since Alpha doesn't play nicely right now with the
+ // transformation, we rather blend it manually with the background color.
+ ColorStateList textColors = action.getTextColors();
+ int[] colors = textColors.getColors();
+ int[] newColors = new int[colors.length];
+ float disabledAlpha = mView.getResources().getFloat(
+ com.android.internal.R.dimen.notification_action_disabled_alpha);
+ for (int j = 0; j < colors.length; j++) {
+ int color = colors[j];
+ color = blendColorWithBackground(color, disabledAlpha);
+ newColors[j] = color;
+ }
+ ColorStateList newColorStateList = new ColorStateList(
+ textColors.getStates(), newColors);
+ action.setTextColor(newColorStateList);
+ }
+ }
+
+ /**
+ * Returns the hashcode of underlying target of PendingIntent. We can get multiple
+ * Java PendingIntent wrapper objects pointing to the same cancelled PI in system_server.
+ * This makes sure we treat them equally.
+ */
+ private static int getHashCodeForPendingIntent(PendingIntent pendingIntent) {
+ return System.identityHashCode(pendingIntent.getTarget().asBinder());
+ }
+
+ /**
+ * Returns PendingIntent contained in the action tag. May be null.
+ */
+ @Nullable
+ private static PendingIntent getPendingIntentForAction(View action) {
+ return (PendingIntent) action.getTag(com.android.internal.R.id.pending_intent_tag);
+ }
+
+ /**
+ * Registers listeners for pending intent cancellation when Action views are attached
+ * to window.
+ * It calls onCancelPendingIntentForActionView when a PendingIntent is cancelled.
+ */
+ @VisibleForTesting
+ static final class ActionPendingIntentCancellationHandler
+ implements View.OnAttachStateChangeListener {
+
+ @Nullable
+ private static UiOffloadThread sUiOffloadThread = null;
+
+ @NonNull
+ private static UiOffloadThread getUiOffloadThread() {
+ if (sUiOffloadThread == null) {
+ sUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ }
+ return sUiOffloadThread;
+ }
+
+ private final View mView;
+ private final Consumer<PendingIntent> mOnCancelledCallback;
+
+ private final PendingIntent mPendingIntent;
+
+ ActionPendingIntentCancellationHandler(PendingIntent pendingIntent, View actionView,
+ Consumer<PendingIntent> onCancelled) {
+ this.mPendingIntent = pendingIntent;
+ this.mView = actionView;
+ this.mOnCancelledCallback = onCancelled;
+ }
+
+ private final PendingIntent.CancelListener mCancelListener =
+ new PendingIntent.CancelListener() {
+ @Override
+ public void onCanceled(PendingIntent pendingIntent) {
+ mView.post(() -> {
+ mOnCancelledCallback.accept(pendingIntent);
+ // We don't need this listener anymore once the intent was cancelled.
+ remove();
+ });
+ }
+ };
+
+ @MainThread
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ // This is safe to call multiple times with the same listener instance.
+ getUiOffloadThread().execute(() -> {
+ mPendingIntent.registerCancelListener(mCancelListener);
+ });
+ }
+
+ @MainThread
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ // This is safe to call multiple times with the same listener instance.
+ getUiOffloadThread().execute(() ->
+ mPendingIntent.unregisterCancelListener(mCancelListener));
+ }
+
+ /**
+ * Removes this listener from callbacks and releases the held PendingIntent.
+ */
+ @MainThread
+ public void remove() {
+ mView.removeOnAttachStateChangeListener(this);
+ if (mView.getTag(R.id.pending_intent_listener_tag) == this) {
+ mView.setTag(R.id.pending_intent_listener_tag, null);
+ }
+ getUiOffloadThread().execute(() ->
+ mPendingIntent.unregisterCancelListener(mCancelListener));
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 3e1f09f..90cba40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -290,10 +290,6 @@
void acquireGestureWakeLock(long time);
- boolean setAppearance(int appearance);
-
- int getBarMode();
-
void resendMessage(int msg);
void resendMessage(Object msg);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index f7ff39c..22b9298 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -36,16 +36,11 @@
import android.util.Slog;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
-import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowInsetsController.Appearance;
-import android.view.WindowInsetsController.Behavior;
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.statusbar.LetterboxDetails;
-import com.android.internal.view.AppearanceRegion;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.res.R;
import com.android.systemui.assist.AssistManager;
@@ -109,7 +104,6 @@
private final UserTracker mUserTracker;
private final boolean mVibrateOnOpening;
private final VibrationEffect mCameraLaunchGestureVibrationEffect;
- private final SystemBarAttributesListener mSystemBarAttributesListener;
private final ActivityStarter mActivityStarter;
private final Lazy<CameraLauncher> mCameraLauncherLazy;
private final QuickSettingsController mQsController;
@@ -149,7 +143,6 @@
Optional<Vibrator> vibratorOptional,
DisableFlagsLogger disableFlagsLogger,
@DisplayId int displayId,
- SystemBarAttributesListener systemBarAttributesListener,
Lazy<CameraLauncher> cameraLauncherLazy,
UserTracker userTracker,
QSHost qsHost,
@@ -187,7 +180,6 @@
mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
mVibratorOptional, resources);
- mSystemBarAttributesListener = systemBarAttributesListener;
mActivityStarter = activityStarter;
}
@@ -457,29 +449,6 @@
mCentralSurfaces.setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, running);
}
-
- @Override
- public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
- AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, @InsetsType int requestedVisibleTypes, String packageName,
- LetterboxDetails[] letterboxDetails) {
- if (displayId != mDisplayId) {
- return;
- }
- // SystemBarAttributesListener should __always__ be the top-level listener for system bar
- // attributes changed.
- mSystemBarAttributesListener.onSystemBarAttributesChanged(
- displayId,
- appearance,
- appearanceRegions,
- navbarColorManagedByIme,
- behavior,
- requestedVisibleTypes,
- packageName,
- letterboxDetails
- );
- }
-
@Override
public void toggleKeyboardShortcutsMenu(int deviceId) {
mCentralSurfaces.resendMessage(new CentralSurfaces.KeyboardShortcutsMessage(deviceId));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index 3cb5e1f..7dc4b96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -85,8 +85,6 @@
override fun getRotation() = 0
override fun setBarStateForTest(state: Int) {}
override fun acquireGestureWakeLock(time: Long) {}
- override fun setAppearance(appearance: Int) = false
- override fun getBarMode() = 0
override fun resendMessage(msg: Int) {}
override fun resendMessage(msg: Any?) {}
override fun setLastCameraLaunchSource(source: Int) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 05beded..3e2f10d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -21,9 +21,6 @@
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
-import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
@@ -33,12 +30,6 @@
import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -92,7 +83,6 @@
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.WindowInsets;
-import android.view.WindowInsetsController.Appearance;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
@@ -208,6 +198,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.data.model.StatusBarMode;
import com.android.systemui.statusbar.data.repository.StatusBarModeRepository;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -222,7 +213,6 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
-import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -329,21 +319,6 @@
}
@Override
- public boolean setAppearance(int appearance) {
- if (mAppearance != appearance) {
- mAppearance = appearance;
- return updateBarMode(barMode(isTransientShown(), appearance));
- }
-
- return false;
- }
-
- @Override
- public int getBarMode() {
- return mStatusBarMode;
- }
-
- @Override
public void resendMessage(int msg) {
mMessageRouter.cancelMessages(msg);
mMessageRouter.sendMessage(msg);
@@ -464,7 +439,6 @@
private final UserInfoControllerImpl mUserInfoControllerImpl;
private final DemoModeController mDemoModeController;
private final NotificationsController mNotificationsController;
- private final OngoingCallController mOngoingCallController;
private final StatusBarSignalPolicy mStatusBarSignalPolicy;
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
private final Lazy<LightRevealScrimViewModel> mLightRevealScrimViewModelLazy;
@@ -497,9 +471,6 @@
private final Provider<FingerprintManager> mFingerprintManager;
private final ActivityStarter mActivityStarter;
- /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
- private @Appearance int mAppearance;
-
private final DisplayMetrics mDisplayMetrics;
// XXX: gesture research
@@ -554,7 +525,6 @@
private final DelayableExecutor mMainExecutor;
private int mInteractingWindows;
- private @TransitionMode int mStatusBarMode;
private final ViewMediatorCallback mKeyguardViewMediatorCallback;
private final ScrimController mScrimController;
@@ -714,7 +684,6 @@
BrightnessSliderController.Factory brightnessSliderFactory,
ScreenOffAnimationController screenOffAnimationController,
WallpaperController wallpaperController,
- OngoingCallController ongoingCallController,
StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
FeatureFlags featureFlags,
@@ -820,7 +789,6 @@
mNotificationIconAreaController = notificationIconAreaController;
mBrightnessSliderFactory = brightnessSliderFactory;
mWallpaperController = wallpaperController;
- mOngoingCallController = ongoingCallController;
mStatusBarSignalPolicy = statusBarSignalPolicy;
mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
mFeatureFlags = featureFlags;
@@ -854,9 +822,6 @@
mActivityIntentHelper = new ActivityIntentHelper(mContext);
mActivityLaunchAnimator = activityLaunchAnimator;
- // The status bar background may need updating when the ongoing call status changes.
- mOngoingCallController.addCallback((animate) -> maybeUpdateBarMode());
-
// TODO(b/190746471): Find a better home for this.
DateTimeView.setReceiverHandler(timeTickHandler);
@@ -1192,8 +1157,8 @@
mJavaAdapter.alwaysCollectFlow(
mStatusBarModeRepository.isTransientShown(), this::onTransientShownChanged);
mJavaAdapter.alwaysCollectFlow(
- mStatusBarModeRepository.isInFullscreenMode(),
- this::onStatusBarFullscreenChanged);
+ mStatusBarModeRepository.getStatusBarMode(),
+ this::updateBarMode);
mCommandQueueCallbacks = mCommandQueueCallbacksLazy.get();
mCommandQueue.addCallback(mCommandQueueCallbacks);
@@ -1712,50 +1677,12 @@
if (transientShown) {
mNoAnimationOnNextBarModeChange = true;
}
- maybeUpdateBarMode();
}
- private void onStatusBarFullscreenChanged(boolean isWindowShown) {
- maybeUpdateBarMode();
- }
-
- private void maybeUpdateBarMode() {
- final int barMode = barMode(isTransientShown(), mAppearance);
- if (updateBarMode(barMode)) {
- mLightBarController.onStatusBarModeChanged(barMode);
- updateBubblesVisibility();
- }
- }
-
- private boolean updateBarMode(int barMode) {
- if (mStatusBarMode != barMode) {
- mStatusBarMode = barMode;
- checkBarModes();
- mAutoHideController.touchAutoHide();
- return true;
- }
- return false;
- }
-
- private @TransitionMode int barMode(boolean isTransient, int appearance) {
- boolean isFullscreen = mStatusBarModeRepository.isInFullscreenMode().getValue();
- final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
- if (mOngoingCallController.hasOngoingCall() && isFullscreen) {
- // Force show the status bar if there's an ongoing call.
- return MODE_SEMI_TRANSPARENT;
- } else if (isTransient) {
- return MODE_SEMI_TRANSPARENT;
- } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) {
- return MODE_LIGHTS_OUT;
- } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) {
- return MODE_LIGHTS_OUT_TRANSPARENT;
- } else if ((appearance & APPEARANCE_OPAQUE_STATUS_BARS) != 0) {
- return MODE_OPAQUE;
- } else if ((appearance & APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS) != 0) {
- return MODE_SEMI_TRANSPARENT;
- } else {
- return MODE_TRANSPARENT;
- }
+ private void updateBarMode(StatusBarMode barMode) {
+ checkBarModes();
+ mAutoHideController.touchAutoHide();
+ updateBubblesVisibility();
}
@Override
@@ -1785,7 +1712,10 @@
public void checkBarModes() {
if (mDemoModeController.isInDemoMode()) return;
if (mStatusBarTransitions != null) {
- checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarTransitions);
+ checkBarMode(
+ mStatusBarModeRepository.getStatusBarMode().getValue(),
+ mStatusBarWindowState,
+ mStatusBarTransitions);
}
mNavigationBarController.checkNavBarModes(mDisplayId);
mNoAnimationOnNextBarModeChange = false;
@@ -1794,16 +1724,19 @@
/** Temporarily hides Bubbles if the status bar is hidden. */
@Override
public void updateBubblesVisibility() {
+ StatusBarMode mode = mStatusBarModeRepository.getStatusBarMode().getValue();
mBubblesOptional.ifPresent(bubbles -> bubbles.onStatusBarVisibilityChanged(
- mStatusBarMode != MODE_LIGHTS_OUT
- && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT));
+ mode != StatusBarMode.LIGHTS_OUT
+ && mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT));
}
- void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState,
+ void checkBarMode(
+ StatusBarMode mode,
+ @WindowVisibleState int windowState,
BarTransitions transitions) {
final boolean anim = !mNoAnimationOnNextBarModeChange && mDeviceInteractive
&& windowState != WINDOW_STATE_HIDDEN;
- transitions.transitionTo(mode, anim);
+ transitions.transitionTo(mode.toTransitionModeInt(), anim);
}
private void finishBarAnimations() {
@@ -1850,8 +1783,6 @@
pw.print(" mInteractingWindows="); pw.println(mInteractingWindows);
pw.print(" mStatusBarWindowState=");
pw.println(windowStateToString(mStatusBarWindowState));
- pw.print(" mStatusBarMode=");
- pw.println(BarTransitions.modeToString(mStatusBarMode));
pw.print(" mDozing="); pw.println(mDozing);
pw.print(" mWallpaperSupported= "); pw.println(mWallpaperSupported);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
index a61914a..231a8c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone
import android.annotation.ColorInt
+import android.content.Context
import android.graphics.Rect
import android.view.InsetsFlags
import android.view.ViewDebug
@@ -29,20 +30,17 @@
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
import java.io.PrintWriter
-import java.util.Arrays
import javax.inject.Inject
-class LetterboxAppearance(
+data class LetterboxAppearance(
@Appearance val appearance: Int,
- val appearanceRegions: Array<AppearanceRegion>
+ val appearanceRegions: List<AppearanceRegion>,
) {
override fun toString(): String {
val appearanceString =
ViewDebug.flagsToString(InsetsFlags::class.java, "appearance", appearance)
- return "LetterboxAppearance{$appearanceString, ${appearanceRegions.contentToString()}}"
+ return "LetterboxAppearance{$appearanceString, $appearanceRegions}"
}
}
@@ -54,69 +52,81 @@
class LetterboxAppearanceCalculator
@Inject
constructor(
- private val lightBarController: LightBarController,
+ context: Context,
dumpManager: DumpManager,
private val letterboxBackgroundProvider: LetterboxBackgroundProvider,
-) : OnStatusBarViewInitializedListener, Dumpable {
+) : Dumpable {
+
+ private val darkAppearanceIconColor = context.getColor(
+ // For a dark background status bar, use a *light* icon color.
+ com.android.settingslib.R.color.light_mode_icon_color_single_tone
+ )
+ private val lightAppearanceIconColor = context.getColor(
+ // For a light background status bar, use a *dark* icon color.
+ com.android.settingslib.R.color.dark_mode_icon_color_single_tone
+ )
init {
dumpManager.registerCriticalDumpable(this)
}
- private var statusBarBoundsProvider: StatusBarBoundsProvider? = null
-
private var lastAppearance: Int? = null
- private var lastAppearanceRegions: Array<AppearanceRegion>? = null
- private var lastLetterboxes: Array<LetterboxDetails>? = null
+ private var lastAppearanceRegions: List<AppearanceRegion>? = null
+ private var lastLetterboxes: List<LetterboxDetails>? = null
private var lastLetterboxAppearance: LetterboxAppearance? = null
fun getLetterboxAppearance(
@Appearance originalAppearance: Int,
- originalAppearanceRegions: Array<AppearanceRegion>,
- letterboxes: Array<LetterboxDetails>
+ originalAppearanceRegions: List<AppearanceRegion>,
+ letterboxes: List<LetterboxDetails>,
+ statusBarBounds: BoundsPair,
): LetterboxAppearance {
lastAppearance = originalAppearance
lastAppearanceRegions = originalAppearanceRegions
lastLetterboxes = letterboxes
return getLetterboxAppearanceInternal(
- letterboxes, originalAppearance, originalAppearanceRegions)
+ letterboxes, originalAppearance, originalAppearanceRegions, statusBarBounds)
.also { lastLetterboxAppearance = it }
}
private fun getLetterboxAppearanceInternal(
- letterboxes: Array<LetterboxDetails>,
+ letterboxes: List<LetterboxDetails>,
originalAppearance: Int,
- originalAppearanceRegions: Array<AppearanceRegion>
+ originalAppearanceRegions: List<AppearanceRegion>,
+ statusBarBounds: BoundsPair,
): LetterboxAppearance {
- if (isScrimNeeded(letterboxes)) {
+ if (isScrimNeeded(letterboxes, statusBarBounds)) {
return originalAppearanceWithScrim(originalAppearance, originalAppearanceRegions)
}
val appearance = appearanceWithoutScrim(originalAppearance)
val appearanceRegions = getAppearanceRegions(originalAppearanceRegions, letterboxes)
- return LetterboxAppearance(appearance, appearanceRegions.toTypedArray())
+ return LetterboxAppearance(appearance, appearanceRegions)
}
- private fun isScrimNeeded(letterboxes: Array<LetterboxDetails>): Boolean {
+ private fun isScrimNeeded(
+ letterboxes: List<LetterboxDetails>,
+ statusBarBounds: BoundsPair,
+ ): Boolean {
if (isOuterLetterboxMultiColored()) {
return true
}
return letterboxes.any { letterbox ->
- letterbox.letterboxInnerBounds.overlapsWith(getStartSideIconBounds()) ||
- letterbox.letterboxInnerBounds.overlapsWith(getEndSideIconsBounds())
+ letterbox.letterboxInnerBounds.overlapsWith(statusBarBounds.start) ||
+ letterbox.letterboxInnerBounds.overlapsWith(statusBarBounds.end)
}
}
private fun getAppearanceRegions(
- originalAppearanceRegions: Array<AppearanceRegion>,
- letterboxes: Array<LetterboxDetails>
+ originalAppearanceRegions: List<AppearanceRegion>,
+ letterboxes: List<LetterboxDetails>
): List<AppearanceRegion> {
return sanitizeAppearanceRegions(originalAppearanceRegions, letterboxes) +
getAllOuterAppearanceRegions(letterboxes)
}
private fun sanitizeAppearanceRegions(
- originalAppearanceRegions: Array<AppearanceRegion>,
- letterboxes: Array<LetterboxDetails>
+ originalAppearanceRegions: List<AppearanceRegion>,
+ letterboxes: List<LetterboxDetails>
): List<AppearanceRegion> =
originalAppearanceRegions.map { appearanceRegion ->
val matchingLetterbox =
@@ -134,7 +144,7 @@
private fun originalAppearanceWithScrim(
@Appearance originalAppearance: Int,
- originalAppearanceRegions: Array<AppearanceRegion>
+ originalAppearanceRegions: List<AppearanceRegion>
): LetterboxAppearance {
return LetterboxAppearance(
originalAppearance or APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS,
@@ -146,7 +156,7 @@
originalAppearance and APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS.inv()
private fun getAllOuterAppearanceRegions(
- letterboxes: Array<LetterboxDetails>
+ letterboxes: List<LetterboxDetails>
): List<AppearanceRegion> = letterboxes.map(this::getOuterAppearanceRegions).flatten()
private fun getOuterAppearanceRegions(
@@ -172,11 +182,9 @@
private fun getOuterAppearance(): Int {
val backgroundColor = outerLetterboxBackgroundColor()
val darkAppearanceContrast =
- ContrastColorUtil.calculateContrast(
- lightBarController.darkAppearanceIconColor, backgroundColor)
+ ContrastColorUtil.calculateContrast(darkAppearanceIconColor, backgroundColor)
val lightAppearanceContrast =
- ContrastColorUtil.calculateContrast(
- lightBarController.lightAppearanceIconColor, backgroundColor)
+ ContrastColorUtil.calculateContrast(lightAppearanceIconColor, backgroundColor)
return if (lightAppearanceContrast > darkAppearanceContrast) {
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
} else {
@@ -193,18 +201,6 @@
return letterboxBackgroundProvider.isLetterboxBackgroundMultiColored
}
- private fun getEndSideIconsBounds(): Rect {
- return statusBarBoundsProvider?.visibleEndSideBounds ?: Rect()
- }
-
- private fun getStartSideIconBounds(): Rect {
- return statusBarBoundsProvider?.visibleStartSideBounds ?: Rect()
- }
-
- override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) {
- statusBarBoundsProvider = component.boundsProvider
- }
-
private fun Rect.overlapsWith(other: Rect): Boolean {
if (this.contains(other) || other.contains(this)) {
return false
@@ -216,8 +212,8 @@
pw.println(
"""
lastAppearance: ${lastAppearance?.toAppearanceString()}
- lastAppearanceRegion: ${Arrays.toString(lastAppearanceRegions)},
- lastLetterboxes: ${Arrays.toString(lastLetterboxes)},
+ lastAppearanceRegion: $lastAppearanceRegions,
+ lastLetterboxes: $lastLetterboxes,
lastLetterboxAppearance: $lastLetterboxAppearance
""".trimIndent())
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxModule.kt
index 61377e2..2e3f0d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxModule.kt
@@ -18,12 +18,10 @@
package com.android.systemui.statusbar.phone
import com.android.systemui.CoreStartable
-import com.android.systemui.statusbar.core.StatusBarInitializer
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
-import dagger.multibindings.IntoSet
@Module
abstract class LetterboxModule {
@@ -31,10 +29,4 @@
@IntoMap
@ClassKey(LetterboxBackgroundProvider::class)
abstract fun bindFeature(impl: LetterboxBackgroundProvider): CoreStartable
-
- @Binds
- @IntoSet
- abstract fun statusBarInitializedListener(
- letterboxAppearanceCalculator: LetterboxAppearanceCalculator
- ): StatusBarInitializer.OnStatusBarViewInitializedListener
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index c2dd059..4d3e2ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -22,7 +22,6 @@
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
-import android.annotation.ColorInt;
import android.content.Context;
import android.graphics.Rect;
import android.util.Log;
@@ -31,17 +30,22 @@
import android.view.WindowInsetsController.Appearance;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.view.AppearanceRegion;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.statusbar.data.model.StatusBarAppearance;
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepository;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.Compile;
+import com.android.systemui.util.kotlin.JavaAdapter;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -52,7 +56,8 @@
* Controls how light status bar flag applies to the icons.
*/
@SysUISingleton
-public class LightBarController implements BatteryController.BatteryStateChangeCallback, Dumpable {
+public class LightBarController implements
+ BatteryController.BatteryStateChangeCallback, Dumpable, CoreStartable {
private static final String TAG = "LightBarController";
private static final boolean DEBUG_NAVBAR = Compile.IS_DEBUG;
@@ -60,18 +65,19 @@
private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f;
+ private final JavaAdapter mJavaAdapter;
private final SysuiDarkIconDispatcher mStatusBarIconController;
private final BatteryController mBatteryController;
+ private final StatusBarModeRepository mStatusBarModeRepository;
private BiometricUnlockController mBiometricUnlockController;
private LightBarTransitionsController mNavigationBarController;
private @Appearance int mAppearance;
private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0];
private int mStatusBarMode;
+ private BoundsPair mStatusBarBounds = new BoundsPair(new Rect(), new Rect());
private int mNavigationBarMode;
private int mNavigationMode;
- private final int mDarkIconColor;
- private final int mLightIconColor;
/**
* Whether the navigation bar should be light factoring in already how much alpha the scrim has.
@@ -116,18 +122,18 @@
@Inject
public LightBarController(
Context ctx,
+ JavaAdapter javaAdapter,
DarkIconDispatcher darkIconDispatcher,
BatteryController batteryController,
NavigationModeController navModeController,
+ StatusBarModeRepository statusBarModeRepository,
DumpManager dumpManager,
DisplayTracker displayTracker) {
- mDarkIconColor = ctx.getColor(
- com.android.settingslib.R.color.dark_mode_icon_color_single_tone);
- mLightIconColor = ctx.getColor(
- com.android.settingslib.R.color.light_mode_icon_color_single_tone);
+ mJavaAdapter = javaAdapter;
mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
mBatteryController = batteryController;
mBatteryController.addCallback(this);
+ mStatusBarModeRepository = statusBarModeRepository;
mNavigationMode = navModeController.addListener((mode) -> {
mNavigationMode = mode;
});
@@ -137,14 +143,11 @@
}
}
- @ColorInt
- int getLightAppearanceIconColor() {
- return mDarkIconColor;
- }
-
- @ColorInt
- int getDarkAppearanceIconColor() {
- return mLightIconColor;
+ @Override
+ public void start() {
+ mJavaAdapter.alwaysCollectFlow(
+ mStatusBarModeRepository.getStatusBarAppearance(),
+ this::onStatusBarAppearanceChanged);
}
public void setNavigationBar(LightBarTransitionsController navigationBar) {
@@ -157,26 +160,48 @@
mBiometricUnlockController = biometricUnlockController;
}
- void onStatusBarAppearanceChanged(AppearanceRegion[] appearanceRegions, boolean sbModeChanged,
- int statusBarMode, boolean navbarColorManagedByIme) {
+ private void onStatusBarAppearanceChanged(@Nullable StatusBarAppearance params) {
+ if (params == null) {
+ return;
+ }
+ int newStatusBarMode = params.getMode().toTransitionModeInt();
+ boolean sbModeChanged = mStatusBarMode != newStatusBarMode;
+ mStatusBarMode = newStatusBarMode;
+
+ boolean sbBoundsChanged = !mStatusBarBounds.equals(params.getBounds());
+ mStatusBarBounds = params.getBounds();
+
+ onStatusBarAppearanceChanged(
+ params.getAppearanceRegions().toArray(new AppearanceRegion[0]),
+ sbModeChanged,
+ sbBoundsChanged,
+ params.getNavbarColorManagedByIme());
+ }
+
+ private void onStatusBarAppearanceChanged(
+ AppearanceRegion[] appearanceRegions,
+ boolean sbModeChanged,
+ boolean sbBoundsChanged,
+ boolean navbarColorManagedByIme) {
final int numStacks = appearanceRegions.length;
boolean stackAppearancesChanged = mAppearanceRegions.length != numStacks;
for (int i = 0; i < numStacks && !stackAppearancesChanged; i++) {
stackAppearancesChanged |= !appearanceRegions[i].equals(mAppearanceRegions[i]);
}
- if (stackAppearancesChanged || sbModeChanged || mIsCustomizingForBackNav) {
+
+ if (stackAppearancesChanged
+ || sbModeChanged
+ // Be sure to re-draw when the status bar bounds have changed because the status bar
+ // icons may have moved to be part of a different appearance region. See b/301605450
+ || sbBoundsChanged
+ || mIsCustomizingForBackNav) {
mAppearanceRegions = appearanceRegions;
- onStatusBarModeChanged(statusBarMode);
+ updateStatus(mAppearanceRegions);
mIsCustomizingForBackNav = false;
}
mNavbarColorManagedByIme = navbarColorManagedByIme;
}
- void onStatusBarModeChanged(int newBarMode) {
- mStatusBarMode = newBarMode;
- updateStatus(mAppearanceRegions);
- }
-
public void onNavigationBarAppearanceChanged(@Appearance int appearance, boolean nbModeChanged,
int navigationBarMode, boolean navbarColorManagedByIme) {
int diff = appearance ^ mAppearance;
@@ -224,7 +249,10 @@
}
private void reevaluate() {
- onStatusBarAppearanceChanged(mAppearanceRegions, true /* sbModeChange */, mStatusBarMode,
+ onStatusBarAppearanceChanged(
+ mAppearanceRegions,
+ /* sbModeChanged= */ true,
+ /* sbBoundsChanged= */ true,
mNavbarColorManagedByIme);
onNavigationBarAppearanceChanged(mAppearance, true /* nbModeChanged */,
mNavigationBarMode, mNavbarColorManagedByIme);
@@ -444,31 +472,43 @@
* Injectable factory for creating a {@link LightBarController}.
*/
public static class Factory {
+ private final JavaAdapter mJavaAdapter;
private final DarkIconDispatcher mDarkIconDispatcher;
private final BatteryController mBatteryController;
private final NavigationModeController mNavModeController;
+ private final StatusBarModeRepository mStatusBarModeRepository;
private final DumpManager mDumpManager;
private final DisplayTracker mDisplayTracker;
@Inject
public Factory(
+ JavaAdapter javaAdapter,
DarkIconDispatcher darkIconDispatcher,
BatteryController batteryController,
NavigationModeController navModeController,
+ StatusBarModeRepository statusBarModeRepository,
DumpManager dumpManager,
DisplayTracker displayTracker) {
-
+ mJavaAdapter = javaAdapter;
mDarkIconDispatcher = darkIconDispatcher;
mBatteryController = batteryController;
mNavModeController = navModeController;
+ mStatusBarModeRepository = statusBarModeRepository;
mDumpManager = dumpManager;
mDisplayTracker = displayTracker;
}
/** Create an {@link LightBarController} */
public LightBarController create(Context context) {
- return new LightBarController(context, mDarkIconDispatcher, mBatteryController,
- mNavModeController, mDumpManager, mDisplayTracker);
+ return new LightBarController(
+ context,
+ mJavaAdapter,
+ mDarkIconDispatcher,
+ mBatteryController,
+ mNavModeController,
+ mStatusBarModeRepository,
+ mDumpManager,
+ mDisplayTracker);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt
index f5ba399..00b08f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt
@@ -22,22 +22,34 @@
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.END_SIDE_CONTENT
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.START_SIDE_CONTENT
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope
+import com.android.systemui.util.ListenerSet
import com.android.systemui.util.boundsOnScreen
import javax.inject.Inject
import javax.inject.Named
-/** Provides various bounds within the status bar. */
+/**
+ * Provides the bounds of the **content** on each side of the status bar.
+ *
+ * This is distinct from [StatusBarContentInsetsProvider], which provides the bounds of full status
+ * bar after accounting for system insets.
+ */
@StatusBarFragmentScope
class StatusBarBoundsProvider
@Inject
constructor(
- private val changeListeners: Set<@JvmSuppressWildcards BoundsChangeListener>,
@Named(START_SIDE_CONTENT) private val startSideContent: View,
@Named(END_SIDE_CONTENT) private val endSideContent: View,
) : StatusBarFragmentComponent.Startable {
interface BoundsChangeListener {
- fun onStatusBarBoundsChanged()
+ fun onStatusBarBoundsChanged(bounds: BoundsPair)
+ }
+
+ private val changeListeners = ListenerSet<BoundsChangeListener>()
+
+ fun addChangeListener(listener: BoundsChangeListener) {
+ changeListeners.addIfAbsent(listener)
+ listener.onStatusBarBoundsChanged(previousBounds)
}
private var previousBounds =
@@ -48,7 +60,7 @@
val newBounds = BoundsPair(start = visibleStartSideBounds, end = visibleEndSideBounds)
if (previousBounds != newBounds) {
previousBounds = newBounds
- changeListeners.forEach { it.onStatusBarBoundsChanged() }
+ changeListeners.forEach { it.onStatusBarBoundsChanged(newBounds) }
}
}
@@ -89,4 +101,10 @@
get() = startSideContent.boundsOnScreen
}
-private data class BoundsPair(val start: Rect, val end: Rect)
+/**
+ * Stores bounds of the status content.
+ *
+ * @property start the bounds of the status bar content on the start side (clock & notif icons).
+ * @property end the bounds of the status bar content on the end side (system icons & battery).
+ */
+data class BoundsPair(val start: Rect, val end: Rect)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 8d86d72..eedf35f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static android.view.WindowInsets.Type.navigationBars;
+
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -66,7 +67,6 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
import com.android.systemui.keyguard.shared.model.DismissAction;
@@ -134,7 +134,7 @@
// dranw its first frame.
private static final long KEYGUARD_DISMISS_DURATION_LOCKED = 2000;
- private static String TAG = "StatusBarKeyguardViewManager";
+ private static final String TAG = "StatusBarKeyguardViewManager";
private static final boolean DEBUG = false;
protected final Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
deleted file mode 100644
index 829577b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
+++ /dev/null
@@ -1,161 +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.systemui.statusbar.phone
-
-import android.view.InsetsFlags
-import android.view.ViewDebug
-import android.view.WindowInsets.Type.InsetsType
-import android.view.WindowInsetsController.Appearance
-import android.view.WindowInsetsController.Behavior
-import com.android.internal.statusbar.LetterboxDetails
-import com.android.internal.view.AppearanceRegion
-import com.android.systemui.Dumpable
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dump.DumpManager
-import java.io.PrintWriter
-import javax.inject.Inject
-
-/**
- * Top-level listener of system attributes changed. This class is __always the first__ one to be
- * notified about changes.
- *
- * It is responsible for modifying any attributes if necessary, and then notifying the other
- * downstream listeners.
- */
-@SysUISingleton
-class SystemBarAttributesListener
-@Inject
-internal constructor(
- private val centralSurfaces: CentralSurfaces,
- private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator,
- private val lightBarController: LightBarController,
- dumpManager: DumpManager,
-) : Dumpable, StatusBarBoundsProvider.BoundsChangeListener {
-
- private var lastLetterboxAppearance: LetterboxAppearance? = null
- private var lastSystemBarAttributesParams: SystemBarAttributesParams? = null
-
- init {
- dumpManager.registerCriticalDumpable(this)
- }
-
- override fun onStatusBarBoundsChanged() {
- val params = lastSystemBarAttributesParams
- if (params != null && shouldUseLetterboxAppearance(params.letterboxesArray)) {
- onSystemBarAttributesChanged(
- params.displayId,
- params.appearance,
- params.appearanceRegionsArray,
- params.navbarColorManagedByIme,
- params.behavior,
- params.requestedVisibleTypes,
- params.packageName,
- params.letterboxesArray)
- }
- }
-
- fun onSystemBarAttributesChanged(
- displayId: Int,
- @Appearance originalAppearance: Int,
- originalAppearanceRegions: Array<AppearanceRegion>,
- navbarColorManagedByIme: Boolean,
- @Behavior behavior: Int,
- @InsetsType requestedVisibleTypes: Int,
- packageName: String,
- letterboxDetails: Array<LetterboxDetails>
- ) {
- lastSystemBarAttributesParams =
- SystemBarAttributesParams(
- displayId,
- originalAppearance,
- originalAppearanceRegions.toList(),
- navbarColorManagedByIme,
- behavior,
- requestedVisibleTypes,
- packageName,
- letterboxDetails.toList())
-
- val (appearance, appearanceRegions) =
- modifyAppearanceIfNeeded(
- originalAppearance, originalAppearanceRegions, letterboxDetails)
-
- val barModeChanged = centralSurfaces.setAppearance(appearance)
-
- lightBarController.onStatusBarAppearanceChanged(
- appearanceRegions, barModeChanged, centralSurfaces.barMode, navbarColorManagedByIme)
-
- centralSurfaces.updateBubblesVisibility()
- }
-
- private fun modifyAppearanceIfNeeded(
- appearance: Int,
- appearanceRegions: Array<AppearanceRegion>,
- letterboxDetails: Array<LetterboxDetails>
- ): Pair<Int, Array<AppearanceRegion>> =
- if (shouldUseLetterboxAppearance(letterboxDetails)) {
- val letterboxAppearance =
- letterboxAppearanceCalculator.getLetterboxAppearance(
- appearance, appearanceRegions, letterboxDetails)
- lastLetterboxAppearance = letterboxAppearance
- Pair(letterboxAppearance.appearance, letterboxAppearance.appearanceRegions)
- } else {
- lastLetterboxAppearance = null
- Pair(appearance, appearanceRegions)
- }
-
- private fun shouldUseLetterboxAppearance(letterboxDetails: Array<LetterboxDetails>) =
- letterboxDetails.isNotEmpty()
-
- override fun dump(printWriter: PrintWriter, strings: Array<String>) {
- printWriter.println("lastSystemBarAttributesParams: $lastSystemBarAttributesParams")
- printWriter.println("lastLetterboxAppearance: $lastLetterboxAppearance")
- }
-}
-
-/**
- * Keeps track of the parameters passed in
- * [SystemBarAttributesListener.onSystemBarAttributesChanged].
- */
-private data class SystemBarAttributesParams(
- val displayId: Int,
- @Appearance val appearance: Int,
- val appearanceRegions: List<AppearanceRegion>,
- val navbarColorManagedByIme: Boolean,
- @Behavior val behavior: Int,
- @InsetsType val requestedVisibleTypes: Int,
- val packageName: String,
- val letterboxes: List<LetterboxDetails>,
-) {
- val letterboxesArray = letterboxes.toTypedArray()
- val appearanceRegionsArray = appearanceRegions.toTypedArray()
- override fun toString(): String {
- val appearanceToString =
- ViewDebug.flagsToString(InsetsFlags::class.java, "appearance", appearance)
- return """SystemBarAttributesParams(
- displayId=$displayId,
- appearance=$appearanceToString,
- appearanceRegions=$appearanceRegions,
- navbarColorManagedByIme=$navbarColorManagedByIme,
- behavior=$behavior,
- requestedVisibleTypes=$requestedVisibleTypes,
- packageName='$packageName',
- letterboxes=$letterboxes,
- letterboxesArray=${letterboxesArray.contentToString()},
- appearanceRegionsArray=${appearanceRegionsArray.contentToString()}
- )""".trimMargin()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index 07c23d1..6ef877b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -26,22 +26,16 @@
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
-import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
import com.android.systemui.statusbar.phone.StatusBarLocation;
-import com.android.systemui.statusbar.phone.SystemBarAttributesListener;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.window.StatusBarWindowController;
-import dagger.Binds;
import dagger.Module;
import dagger.Provides;
-import dagger.multibindings.IntoSet;
-import dagger.multibindings.Multibinds;
import java.util.Optional;
-import java.util.Set;
import javax.inject.Named;
@@ -159,14 +153,4 @@
static HeadsUpStatusBarView providesHeasdUpStatusBarView(@RootView PhoneStatusBarView view) {
return view.findViewById(R.id.heads_up_status_bar_view);
}
-
- /** */
- @Multibinds
- Set<StatusBarBoundsProvider.BoundsChangeListener> boundsChangeListeners();
-
- /** */
- @Binds
- @IntoSet
- StatusBarBoundsProvider.BoundsChangeListener sysBarAttrsListenerAsBoundsListener(
- SystemBarAttributesListener systemBarAttributesListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 4b1e7a4..b0532ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -41,6 +41,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.time.SystemClock
@@ -57,6 +58,7 @@
class OngoingCallController @Inject constructor(
@Application private val scope: CoroutineScope,
private val context: Context,
+ private val ongoingCallRepository: OngoingCallRepository,
private val notifCollection: CommonNotifCollection,
private val systemClock: SystemClock,
private val activityStarter: ActivityStarter,
@@ -207,7 +209,7 @@
statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(true)
}
updateGestureListening()
- mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
+ sendStateChangeEvent()
} else {
// If we failed to update the chip, don't store the call info. Then [hasOngoingCall]
// will return false and we fall back to typical notification handling.
@@ -261,7 +263,7 @@
tearDownChipView()
statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
- mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
+ sendStateChangeEvent()
uidObserver.unregister()
}
@@ -288,6 +290,11 @@
swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
}
+ private fun sendStateChangeEvent() {
+ ongoingCallRepository.setHasOngoingCall(hasOngoingCall())
+ mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
+ }
+
private data class CallNotificationInfo(
val key: String,
val callStartTime: Long,
@@ -374,9 +381,7 @@
if (oldIsCallAppVisible != isCallAppVisible) {
// Animations may be run as a result of the call's state change, so ensure
// the listener is notified on the main thread.
- mainExecutor.execute {
- mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
- }
+ mainExecutor.execute { sendStateChangeEvent() }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
new file mode 100644
index 0000000..da9c45a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/**
+ * Repository storing whether there's current an ongoing call notification.
+ *
+ * This class is used to break a dependency cycle between
+ * [com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController] and
+ * [com.android.systemui.statusbar.data.repository.StatusBarModeRepository]. Instead, those two
+ * classes both refer to this repository.
+ */
+@SysUISingleton
+class OngoingCallRepository @Inject constructor() {
+ private val _hasOngoingCall = MutableStateFlow(false)
+ /** True if there's currently an ongoing call notification and false otherwise. */
+ val hasOngoingCall: StateFlow<Boolean> = _hasOngoingCall.asStateFlow()
+
+ /**
+ * Sets whether there's currently an ongoing call notification. Should only be set from
+ * [com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController].
+ */
+ fun setHasOngoingCall(hasOngoingCall: Boolean) {
+ _hasOngoingCall.value = hasOngoingCall
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
index efa092b..250fe53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
@@ -22,11 +22,12 @@
import androidx.annotation.VisibleForTesting
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
-import com.android.systemui.res.R
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger
+import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.WifiIcons
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
@@ -90,50 +91,56 @@
)}"
)
)
- is WifiNetworkModel.Active -> {
- val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[model.level])
- val contentDescription =
- ContentDescription.Loaded(
- if (model.isValidated) {
- (levelDesc)
- } else {
- "$levelDesc,${context.getString(NO_INTERNET)}"
- }
- )
- Visible(model.toIcon(showHotspotInfo), contentDescription)
- }
+ is WifiNetworkModel.Active -> model.toIcon(showHotspotInfo, context)
}
- @DrawableRes
- private fun WifiNetworkModel.Active.toIcon(showHotspotInfo: Boolean): Int {
- return if (!showHotspotInfo) {
- this.toBasicIcon()
+ private fun WifiNetworkModel.Active.toIcon(
+ showHotspotInfo: Boolean,
+ context: Context,
+ ): Visible {
+ return if (
+ !showHotspotInfo ||
+ this.hotspotDeviceType == WifiNetworkModel.HotspotDeviceType.NONE
+ ) {
+ this.toBasicIcon(context)
} else {
- when (this.hotspotDeviceType) {
- WifiNetworkModel.HotspotDeviceType.NONE -> this.toBasicIcon()
- WifiNetworkModel.HotspotDeviceType.TABLET ->
- com.android.settingslib.R.drawable.ic_hotspot_tablet
- WifiNetworkModel.HotspotDeviceType.LAPTOP ->
- com.android.settingslib.R.drawable.ic_hotspot_laptop
- WifiNetworkModel.HotspotDeviceType.WATCH ->
- com.android.settingslib.R.drawable.ic_hotspot_watch
- WifiNetworkModel.HotspotDeviceType.AUTO ->
- com.android.settingslib.R.drawable.ic_hotspot_auto
- // Use phone as the default drawable
- WifiNetworkModel.HotspotDeviceType.PHONE,
- WifiNetworkModel.HotspotDeviceType.UNKNOWN,
- WifiNetworkModel.HotspotDeviceType.INVALID ->
- com.android.settingslib.R.drawable.ic_hotspot_phone
- }
+ val icon =
+ when (this.hotspotDeviceType) {
+ WifiNetworkModel.HotspotDeviceType.TABLET ->
+ com.android.settingslib.R.drawable.ic_hotspot_tablet
+ WifiNetworkModel.HotspotDeviceType.LAPTOP ->
+ com.android.settingslib.R.drawable.ic_hotspot_laptop
+ WifiNetworkModel.HotspotDeviceType.WATCH ->
+ com.android.settingslib.R.drawable.ic_hotspot_watch
+ WifiNetworkModel.HotspotDeviceType.AUTO ->
+ com.android.settingslib.R.drawable.ic_hotspot_auto
+ // Use phone as the default drawable
+ WifiNetworkModel.HotspotDeviceType.PHONE,
+ WifiNetworkModel.HotspotDeviceType.UNKNOWN,
+ WifiNetworkModel.HotspotDeviceType.INVALID ->
+ com.android.settingslib.R.drawable.ic_hotspot_phone
+ WifiNetworkModel.HotspotDeviceType.NONE ->
+ throw IllegalStateException("NONE checked earlier")
+ }
+ Visible(
+ icon,
+ ContentDescription.Loaded(context.getString(WIFI_OTHER_DEVICE_CONNECTION)),
+ )
}
}
- @DrawableRes
- private fun WifiNetworkModel.Active.toBasicIcon(): Int {
+ private fun WifiNetworkModel.Active.toBasicIcon(context: Context): Visible {
+ val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[this.level])
return if (this.isValidated) {
- WifiIcons.WIFI_FULL_ICONS[this.level]
+ Visible(
+ WifiIcons.WIFI_FULL_ICONS[this.level],
+ ContentDescription.Loaded(levelDesc),
+ )
} else {
- WifiIcons.WIFI_NO_INTERNET_ICONS[this.level]
+ Visible(
+ WifiIcons.WIFI_NO_INTERNET_ICONS[this.level],
+ ContentDescription.Loaded("$levelDesc,${context.getString(NO_INTERNET)}"),
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index ae8128d..27f8121 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -67,7 +67,7 @@
private final ToastLogger mToastLogger;
@Nullable private ToastPresenter mPresenter;
@Nullable private ITransientNotificationCallback mCallback;
- private ToastOutAnimatorListener mToastOutAnimatorListener;
+ @VisibleForTesting ToastOutAnimatorListener mToastOutAnimatorListener;
@VisibleForTesting SystemUIToast mToast;
private int mOrientation = ORIENTATION_PORTRAIT;
@@ -172,7 +172,7 @@
if (mToast.getOutAnimation() != null) {
Animator animator = mToast.getOutAnimation();
mToastOutAnimatorListener = new ToastOutAnimatorListener(mPresenter, mCallback,
- runnable);
+ runnable, animator);
animator.addListener(mToastOutAnimatorListener);
animator.start();
} else {
@@ -211,14 +211,17 @@
final ToastPresenter mPrevPresenter;
final ITransientNotificationCallback mPrevCallback;
@Nullable Runnable mShowNextToastRunnable;
+ @NonNull private final Animator mAnimator;
ToastOutAnimatorListener(
@NonNull ToastPresenter presenter,
@NonNull ITransientNotificationCallback callback,
- @Nullable Runnable runnable) {
+ @Nullable Runnable runnable,
+ @NonNull Animator animator) {
mPrevPresenter = presenter;
mPrevCallback = callback;
mShowNextToastRunnable = runnable;
+ mAnimator = animator;
}
void setShowNextToastRunnable(Runnable runnable) {
@@ -231,6 +234,8 @@
if (mShowNextToastRunnable != null) {
mShowNextToastRunnable.run();
}
+ mAnimator.removeListener(this);
+ mShowNextToastRunnable = null;
mToastOutAnimatorListener = null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 727d649..3c6d90d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -135,6 +135,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.AlphaTintDrawableWrapper;
import com.android.systemui.util.RoundedCornerProgressDrawable;
+import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -304,6 +305,8 @@
private @DevicePostureController.DevicePostureInt int mDevicePosture;
private int mOrientation;
private final FeatureFlags mFeatureFlags;
+ private final SecureSettings mSecureSettings;
+ private int mDialogTimeoutMillis;
public VolumeDialogImpl(
Context context,
@@ -320,7 +323,8 @@
DevicePostureController devicePostureController,
Looper looper,
DumpManager dumpManager,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ SecureSettings secureSettings) {
mFeatureFlags = featureFlags;
mContext =
new ContextThemeWrapper(context, R.style.volume_dialog_theme);
@@ -351,6 +355,8 @@
mUseBackgroundBlur =
mContext.getResources().getBoolean(R.bool.config_volumeDialogUseBackgroundBlur);
mInteractionJankMonitor = interactionJankMonitor;
+ mSecureSettings = secureSettings;
+ mDialogTimeoutMillis = DIALOG_TIMEOUT_MILLIS;
dumpManager.registerDumpable("VolumeDialogImpl", this);
@@ -515,6 +521,8 @@
mDialog.setContentView(R.layout.volume_dialog);
mDialogView = mDialog.findViewById(R.id.volume_dialog);
mDialogView.setAlpha(0);
+ mDialogTimeoutMillis = mSecureSettings.getInt(Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT,
+ DIALOG_TIMEOUT_MILLIS);
mDialog.setCanceledOnTouchOutside(true);
mDialog.setOnShowListener(dialog -> {
mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
@@ -527,7 +535,7 @@
.alpha(1)
.translationX(0)
.setDuration(mDialogShowAnimationDurationMs)
- .setListener(getJankListener(getDialogView(), TYPE_SHOW, DIALOG_TIMEOUT_MILLIS))
+ .setListener(getJankListener(getDialogView(), TYPE_SHOW, mDialogTimeoutMillis))
.setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
.withEndAction(() -> {
if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
@@ -1514,7 +1522,7 @@
AccessibilityManager.FLAG_CONTENT_TEXT
| AccessibilityManager.FLAG_CONTENT_CONTROLS);
}
- return mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS,
+ return mAccessibilityMgr.getRecommendedTimeoutMillis(mDialogTimeoutMillis,
AccessibilityManager.FLAG_CONTENT_CONTROLS);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index cc9f3e1..624691b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.volume.CsdWarningDialog;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.volume.VolumeDialogComponent;
@@ -63,7 +64,8 @@
CsdWarningDialog.Factory csdFactory,
DevicePostureController devicePostureController,
DumpManager dumpManager,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ SecureSettings secureSettings) {
VolumeDialogImpl impl = new VolumeDialogImpl(
context,
volumeDialogController,
@@ -79,7 +81,8 @@
devicePostureController,
Looper.getMainLooper(),
dumpManager,
- featureFlags);
+ featureFlags,
+ secureSettings);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
impl.setSilentMode(false);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index f6649bd..d54843d3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -36,10 +36,8 @@
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
-import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate
import com.android.systemui.biometrics.SideFpsController
import com.android.systemui.biometrics.SideFpsUiRequestSource
@@ -47,6 +45,7 @@
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
import com.android.systemui.classifier.FalsingA11yDelegate
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -54,6 +53,7 @@
import com.android.systemui.log.SessionTracker
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -157,7 +157,7 @@
private lateinit var sceneTestUtils: SceneTestUtils
private lateinit var sceneInteractor: SceneInteractor
private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
- private lateinit var authenticationInteractor: AuthenticationInteractor
+ private lateinit var deviceEntryInteractor: DeviceEntryInteractor
@Mock private lateinit var primaryBouncerInteractor: Lazy<PrimaryBouncerInteractor>
private lateinit var sceneTransitionStateFlow: MutableStateFlow<ObservableTransitionState>
@@ -229,10 +229,10 @@
sceneTransitionStateFlow =
MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Lockscreen))
sceneInteractor.setTransitionState(sceneTransitionStateFlow)
- authenticationInteractor =
- sceneTestUtils.authenticationInteractor(
- repository = sceneTestUtils.authenticationRepository(),
- sceneInteractor = sceneInteractor
+ deviceEntryInteractor =
+ sceneTestUtils.deviceEntryInteractor(
+ authenticationInteractor = sceneTestUtils.authenticationInteractor(),
+ sceneInteractor = sceneInteractor,
)
underTest =
@@ -268,7 +268,7 @@
keyguardTransitionInteractor,
primaryBouncerInteractor,
) {
- authenticationInteractor
+ deviceEntryInteractor
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
index ba3dbf0..484b119 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -20,8 +20,10 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.animation.LayoutTransition;
import android.view.View;
import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.keyguard.logging.KeyguardLogger;
@@ -44,6 +46,7 @@
public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
@Mock protected KeyguardStatusView mKeyguardStatusView;
+
@Mock protected KeyguardSliceViewController mKeyguardSliceViewController;
@Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController;
@Mock protected KeyguardStateController mKeyguardStateController;
@@ -61,6 +64,10 @@
protected KeyguardStatusViewController mController;
+ @Mock protected KeyguardClockSwitch mKeyguardClockSwitch;
+ @Mock protected FrameLayout mMediaHostContainer;
+ @Mock protected LayoutTransition mMediaLayoutTransition;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -93,6 +100,8 @@
};
when(mKeyguardStatusView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
+
+ when(mKeyguardClockSwitchController.getView()).thenReturn(mKeyguardClockSwitch);
}
protected void givenViewAttached() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index b8b0198..e4e2b0a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -18,14 +18,18 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.animation.LayoutTransition;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.View;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.ClockConfig;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
@@ -124,4 +128,112 @@
mController.onDestroy();
verify(mDumpManager, times(1)).unregisterDumpable(eq(mController.getInstanceName()));
}
+
+ @Test
+ public void onInit_addsOnLayoutChangeListenerToClockSwitch() {
+ when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
+ mMediaHostContainer);
+
+ mController.onInit();
+
+ ArgumentCaptor<View.OnLayoutChangeListener> captor =
+ ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
+ verify(mKeyguardClockSwitch).addOnLayoutChangeListener(captor.capture());
+ }
+
+ @Test
+ public void onInit_addsOnLayoutChangeListenerToMediaHostContainer() {
+ when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
+ mMediaHostContainer);
+
+ mController.onInit();
+
+ ArgumentCaptor<View.OnLayoutChangeListener> captor =
+ ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
+ verify(mMediaHostContainer).addOnLayoutChangeListener(captor.capture());
+ }
+
+ @Test
+ public void clockSwitchHeightChanged_mediaChangingLayoutTransitionEnabled() {
+ when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
+ mMediaHostContainer);
+
+ mController.onInit();
+
+ ArgumentCaptor<View.OnLayoutChangeListener> captor =
+ ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
+ verify(mKeyguardClockSwitch).addOnLayoutChangeListener(captor.capture());
+
+ // Above here is the same as `onInit_addsOnLayoutChangeListenerToClockSwitch`.
+ // Below here is the actual test.
+
+ View.OnLayoutChangeListener listener = captor.getValue();
+
+ mController.setSplitShadeEnabled(true);
+ when(mKeyguardClockSwitch.getSplitShadeCentered()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.isKeyguardVisible()).thenReturn(true);
+ when(mMediaHostContainer.getVisibility()).thenReturn(View.VISIBLE);
+ when(mMediaHostContainer.getHeight()).thenReturn(200);
+ when(mMediaHostContainer.getLayoutTransition()).thenReturn(mMediaLayoutTransition);
+
+ when(mKeyguardClockSwitch.getHeight()).thenReturn(0);
+ listener.onLayoutChange(mKeyguardClockSwitch, /* left= */ 0, /* top= */ 0, /* right= */
+ 0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */
+ 0, /* oldBottom = */ 200);
+ verify(mMediaLayoutTransition).enableTransitionType(LayoutTransition.CHANGING);
+ }
+
+ @Test
+ public void clockSwitchHeightNotChanged_mediaChangingLayoutTransitionNotEnabled() {
+ when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
+ mMediaHostContainer);
+
+ mController.onInit();
+
+ ArgumentCaptor<View.OnLayoutChangeListener> captor =
+ ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
+ verify(mKeyguardClockSwitch).addOnLayoutChangeListener(captor.capture());
+
+ // Above here is the same as `onInit_addsOnLayoutChangeListenerToClockSwitch`.
+ // Below here is the actual test.
+
+ View.OnLayoutChangeListener listener = captor.getValue();
+
+ mController.setSplitShadeEnabled(true);
+ when(mKeyguardClockSwitch.getSplitShadeCentered()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.isKeyguardVisible()).thenReturn(true);
+ when(mMediaHostContainer.getVisibility()).thenReturn(View.VISIBLE);
+ when(mMediaHostContainer.getHeight()).thenReturn(200);
+ when(mMediaHostContainer.getLayoutTransition()).thenReturn(mMediaLayoutTransition);
+
+ when(mKeyguardClockSwitch.getHeight()).thenReturn(200);
+ listener.onLayoutChange(mKeyguardClockSwitch, /* left= */ 0, /* top= */ 0, /* right= */
+ 0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */
+ 0, /* oldBottom = */ 200);
+ verify(mMediaLayoutTransition, never()).enableTransitionType(LayoutTransition.CHANGING);
+ }
+
+ @Test
+ public void onMediaHostContainerLayout_disablesChangingLayoutTransition() {
+ when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
+ mMediaHostContainer);
+
+ mController.onInit();
+
+ ArgumentCaptor<View.OnLayoutChangeListener> captor =
+ ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
+ verify(mMediaHostContainer).addOnLayoutChangeListener(captor.capture());
+
+ // Above here is the same as `onInit_addsOnLayoutChangeListenerToMediaHostContainer`.
+ // Below here is the actual test.
+
+ View.OnLayoutChangeListener listener = captor.getValue();
+
+ when(mMediaHostContainer.getLayoutTransition()).thenReturn(mMediaLayoutTransition);
+
+ when(mMediaLayoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGING)).thenReturn(
+ true);
+ listener.onLayoutChange(mMediaHostContainer, 1, 2, 3, 4, 1, 2, 3, 4);
+ verify(mMediaLayoutTransition).disableTransitionType(LayoutTransition.CHANGING);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
index 86439e5..58d372c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
@@ -1,5 +1,6 @@
package com.android.keyguard
+import android.animation.LayoutTransition
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -35,6 +36,20 @@
}
@Test
+ fun mediaViewHasLayoutTransitionInDisabledState() {
+ val layoutTransition = (mediaView as ViewGroup).layoutTransition
+ assertThat(layoutTransition).isNotNull()
+ assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGE_APPEARING))
+ .isFalse()
+ assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGE_DISAPPEARING))
+ .isFalse()
+ assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.APPEARING)).isFalse()
+ assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.DISAPPEARING))
+ .isFalse()
+ assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGING)).isFalse()
+ }
+
+ @Test
fun setChildrenTranslationYExcludingMediaView_mediaViewIsNotTranslated() {
val translationY = 1234f
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 187f098..b8d2bdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -88,9 +88,9 @@
import androidx.test.filters.LargeTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.res.R;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.util.leak.ReferenceTestUtils;
import com.android.systemui.util.settings.SecureSettings;
@@ -121,6 +121,9 @@
public class WindowMagnificationControllerTest extends SysuiTestCase {
private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
+ // The duration couldn't too short, otherwise the animation check on bounce effect
+ // won't work in expectation. (b/299537784)
+ private static final int BOUNCE_EFFECT_DURATION_MS = 2000;
private static final long ANIMATION_DURATION_MS = 300;
private final long mWaitingAnimationPeriod = 2 * ANIMATION_DURATION_MS;
@Mock
@@ -205,6 +208,7 @@
mSysUiState,
() -> mWindowSessionSpy,
mSecureSettings);
+ mWindowMagnificationController.setBounceEffectDuration(BOUNCE_EFFECT_DURATION_MS);
verify(mMirrorWindowControl).setWindowDelegate(
any(MirrorWindowControl.MirrorWindowDelegate.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index d3a2a73..0283382 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -74,7 +74,6 @@
getSecurityMode = getSecurityMode,
backgroundDispatcher = testUtils.testDispatcher,
userRepository = userRepository,
- keyguardRepository = testUtils.keyguardRepository,
lockPatternUtils = lockPatternUtils,
broadcastDispatcher = fakeBroadcastDispatcher,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index 874053a..a102890 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -20,15 +20,12 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
-import com.android.systemui.authentication.data.repository.AuthenticationRepository
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@@ -47,13 +44,7 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val repository: AuthenticationRepository = utils.authenticationRepository()
- private val sceneInteractor = utils.sceneInteractor()
- private val underTest =
- utils.authenticationInteractor(
- repository = repository,
- sceneInteractor = sceneInteractor,
- )
+ private val underTest = utils.authenticationInteractor()
@Test
fun authenticationMethod() =
@@ -79,10 +70,10 @@
val authMethod by collectLastValue(underTest.authenticationMethod)
runCurrent()
- utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.None)
- setLockscreenEnabled(true)
- }
+ utils.authenticationRepository.setAuthenticationMethod(
+ DataLayerAuthenticationMethodModel.None
+ )
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Swipe)
assertThat(underTest.getAuthenticationMethod())
@@ -95,10 +86,10 @@
val authMethod by collectLastValue(underTest.authenticationMethod)
runCurrent()
- utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.None)
- setLockscreenEnabled(false)
- }
+ utils.authenticationRepository.setAuthenticationMethod(
+ DataLayerAuthenticationMethodModel.None
+ )
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(false)
assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.None)
assertThat(underTest.getAuthenticationMethod())
@@ -106,130 +97,6 @@
}
@Test
- fun isUnlocked_whenAuthMethodIsNoneAndLockscreenDisabled_isTrue() =
- testScope.runTest {
- val isUnlocked by collectLastValue(underTest.isUnlocked)
- utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.None)
- setLockscreenEnabled(false)
- // Toggle isUnlocked, twice.
- //
- // This is done because the underTest.isUnlocked flow doesn't receive values from
- // just changing the state above; the actual isUnlocked state needs to change to
- // cause the logic under test to "pick up" the current state again.
- //
- // It is done twice to make sure that we don't actually change the isUnlocked state
- // from what it originally was.
- setUnlocked(!utils.authenticationRepository.isUnlocked.value)
- runCurrent()
- setUnlocked(!utils.authenticationRepository.isUnlocked.value)
- runCurrent()
- }
-
- assertThat(isUnlocked).isTrue()
- }
-
- @Test
- fun isUnlocked_whenAuthMethodIsNoneAndLockscreenEnabled_isTrue() =
- testScope.runTest {
- utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.None)
- setLockscreenEnabled(true)
- }
-
- val isUnlocked by collectLastValue(underTest.isUnlocked)
- assertThat(isUnlocked).isTrue()
- }
-
- @Test
- fun canSwipeToDismiss_onLockscreenWithSwipe_isTrue() =
- testScope.runTest {
- utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.None)
- setLockscreenEnabled(true)
- }
- switchToScene(SceneKey.Lockscreen)
-
- val canSwipeToDismiss by collectLastValue(underTest.canSwipeToDismiss)
- assertThat(canSwipeToDismiss).isTrue()
- }
-
- @Test
- fun canSwipeToDismiss_onLockscreenWithPin_isFalse() =
- testScope.runTest {
- utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setLockscreenEnabled(true)
- }
- switchToScene(SceneKey.Lockscreen)
-
- val canSwipeToDismiss by collectLastValue(underTest.canSwipeToDismiss)
- assertThat(canSwipeToDismiss).isFalse()
- }
-
- @Test
- fun canSwipeToDismiss_afterLockscreenDismissedInSwipeMode_isFalse() =
- testScope.runTest {
- utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.None)
- setLockscreenEnabled(true)
- }
- switchToScene(SceneKey.Lockscreen)
- switchToScene(SceneKey.Gone)
-
- val canSwipeToDismiss by collectLastValue(underTest.canSwipeToDismiss)
- assertThat(canSwipeToDismiss).isFalse()
- }
-
- @Test
- fun isAuthenticationRequired_lockedAndSecured_true() =
- testScope.runTest {
- utils.authenticationRepository.apply {
- setUnlocked(false)
- runCurrent()
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Password)
- }
-
- assertThat(underTest.isAuthenticationRequired()).isTrue()
- }
-
- @Test
- fun isAuthenticationRequired_lockedAndNotSecured_false() =
- testScope.runTest {
- utils.authenticationRepository.apply {
- setUnlocked(false)
- runCurrent()
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.None)
- }
-
- assertThat(underTest.isAuthenticationRequired()).isFalse()
- }
-
- @Test
- fun isAuthenticationRequired_unlockedAndSecured_false() =
- testScope.runTest {
- utils.authenticationRepository.apply {
- setUnlocked(true)
- runCurrent()
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Password)
- }
-
- assertThat(underTest.isAuthenticationRequired()).isFalse()
- }
-
- @Test
- fun isAuthenticationRequired_unlockedAndNotSecured_false() =
- testScope.runTest {
- utils.authenticationRepository.apply {
- setUnlocked(true)
- runCurrent()
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.None)
- }
-
- assertThat(underTest.isAuthenticationRequired()).isFalse()
- }
-
- @Test
fun authenticate_withCorrectPin_returnsTrue() =
testScope.runTest {
val isThrottled by collectLastValue(underTest.isThrottled)
@@ -366,7 +233,6 @@
@Test
fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalseAndDoesNotUnlockDevice() =
testScope.runTest {
- val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.apply {
setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
setAutoConfirmEnabled(true)
@@ -378,13 +244,13 @@
)
)
.isEqualTo(AuthenticationResult.FAILED)
+ val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
assertThat(isUnlocked).isFalse()
}
@Test
fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalseAndDoesNotUnlockDevice() =
testScope.runTest {
- val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.apply {
setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
setAutoConfirmEnabled(true)
@@ -396,13 +262,13 @@
)
)
.isEqualTo(AuthenticationResult.FAILED)
+ val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
assertThat(isUnlocked).isFalse()
}
@Test
fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrueAndUnlocksDevice() =
testScope.runTest {
- val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.apply {
setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
setAutoConfirmEnabled(true)
@@ -414,13 +280,13 @@
)
)
.isEqualTo(AuthenticationResult.SUCCEEDED)
+ val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
assertThat(isUnlocked).isTrue()
}
@Test
fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNullAndHasNoEffects() =
testScope.runTest {
- val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.apply {
setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
setAutoConfirmEnabled(false)
@@ -432,26 +298,27 @@
)
)
.isEqualTo(AuthenticationResult.SKIPPED)
+ val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
assertThat(isUnlocked).isFalse()
}
@Test
fun tryAutoConfirm_withoutCorrectPassword_returnsNullAndHasNoEffects() =
testScope.runTest {
- val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
DataLayerAuthenticationMethodModel.Password
)
assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true))
.isEqualTo(AuthenticationResult.SKIPPED)
+ val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
assertThat(isUnlocked).isFalse()
}
@Test
fun throttling() =
testScope.runTest {
- val isUnlocked by collectLastValue(underTest.isUnlocked)
+ val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
val throttling by collectLastValue(underTest.throttling)
val isThrottled by collectLastValue(underTest.isThrottled)
utils.authenticationRepository.setAuthenticationMethod(
@@ -462,7 +329,7 @@
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
assertThat(isUnlocked).isFalse()
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
@@ -605,8 +472,4 @@
assertThat(hintedPinLength).isNull()
}
-
- private fun switchToScene(sceneKey: SceneKey) {
- sceneInteractor.changeScene(SceneModel(sceneKey), "reason")
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 92c8a39..a9ba36a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -47,13 +47,16 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val authenticationInteractor =
- utils.authenticationInteractor(
- repository = utils.authenticationRepository(),
- )
+ private val authenticationInteractor = utils.authenticationInteractor()
private val sceneInteractor = utils.sceneInteractor()
+ private val deviceEntryInteractor =
+ utils.deviceEntryInteractor(
+ authenticationInteractor = authenticationInteractor,
+ sceneInteractor = sceneInteractor,
+ )
private val underTest =
utils.bouncerInteractor(
+ deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
)
@@ -76,7 +79,7 @@
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
runCurrent()
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
underTest.showOrUnlockDevice()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
@@ -111,7 +114,7 @@
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
runCurrent()
utils.authenticationRepository.setAutoConfirmEnabled(true)
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
underTest.showOrUnlockDevice()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
@@ -148,7 +151,7 @@
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
runCurrent()
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
underTest.showOrUnlockDevice()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.clearMessage()
@@ -180,7 +183,7 @@
AuthenticationMethodModel.Password
)
runCurrent()
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
underTest.showOrUnlockDevice()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
@@ -215,7 +218,7 @@
AuthenticationMethodModel.Pattern
)
runCurrent()
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
underTest.showOrUnlockDevice()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
@@ -268,7 +271,7 @@
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(true)
+ utils.deviceEntryRepository.setUnlocked(true)
runCurrent()
underTest.showOrUnlockDevice()
@@ -281,8 +284,8 @@
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.authenticationRepository.setLockscreenEnabled(true)
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ utils.deviceEntryRepository.setUnlocked(false)
underTest.showOrUnlockDevice()
@@ -298,7 +301,7 @@
AuthenticationMethodModel.Password
)
runCurrent()
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
val customMessage = "Hello there!"
underTest.showOrUnlockDevice(customMessage)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 2f7dde0..b5177e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -37,17 +37,20 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val authenticationInteractor =
- utils.authenticationInteractor(
- utils.authenticationRepository(),
- )
+ private val authenticationInteractor = utils.authenticationInteractor()
private val sceneInteractor = utils.sceneInteractor()
+ private val deviceEntryInteractor =
+ utils.deviceEntryInteractor(
+ authenticationInteractor = authenticationInteractor,
+ sceneInteractor = sceneInteractor,
+ )
private val underTest =
PinBouncerViewModel(
applicationContext = context,
viewModelScope = testScope.backgroundScope,
interactor =
utils.bouncerInteractor(
+ deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index da2534d6..b75355a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -44,12 +44,15 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val authenticationInteractor =
- utils.authenticationInteractor(
- repository = utils.authenticationRepository,
+ private val authenticationInteractor = utils.authenticationInteractor()
+ private val deviceEntryInteractor =
+ utils.deviceEntryInteractor(
+ authenticationInteractor = authenticationInteractor,
+ sceneInteractor = utils.sceneInteractor(),
)
private val bouncerInteractor =
utils.bouncerInteractor(
+ deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
sceneInteractor = utils.sceneInteractor(),
)
@@ -223,6 +226,8 @@
DataLayerAuthenticationMethodModel.Pattern
}
)
- setLockscreenEnabled(model !is DomainLayerAuthenticationMethodModel.None)
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(
+ model !is DomainLayerAuthenticationMethodModel.None
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index c1b3354..0926399 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -44,13 +44,16 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val authenticationInteractor =
- utils.authenticationInteractor(
- repository = utils.authenticationRepository(),
- )
+ private val authenticationInteractor = utils.authenticationInteractor()
private val sceneInteractor = utils.sceneInteractor()
+ private val deviceEntryInteractor =
+ utils.deviceEntryInteractor(
+ authenticationInteractor = authenticationInteractor,
+ sceneInteractor = utils.sceneInteractor(),
+ )
private val bouncerInteractor =
utils.bouncerInteractor(
+ deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
)
@@ -139,7 +142,7 @@
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password
)
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -207,7 +210,7 @@
private fun TestScope.lockDeviceAndOpenPasswordBouncer() {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Password)
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index bf109d9..2e7c9aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -47,13 +47,16 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val authenticationInteractor =
- utils.authenticationInteractor(
- repository = utils.authenticationRepository(),
- )
+ private val authenticationInteractor = utils.authenticationInteractor()
private val sceneInteractor = utils.sceneInteractor()
+ private val deviceEntryInteractor =
+ utils.deviceEntryInteractor(
+ authenticationInteractor = authenticationInteractor,
+ sceneInteractor = utils.sceneInteractor(),
+ )
private val bouncerInteractor =
utils.bouncerInteractor(
+ deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
)
@@ -378,7 +381,7 @@
private fun TestScope.lockDeviceAndOpenPatternBouncer() {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pattern)
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
assertThat(collectLastValue(sceneInteractor.desiredScene).invoke())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 2576204..255bbe3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -47,12 +47,15 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
- private val authenticationInteractor =
- utils.authenticationInteractor(
- repository = utils.authenticationRepository(),
+ private val authenticationInteractor = utils.authenticationInteractor()
+ private val deviceEntryInteractor =
+ utils.deviceEntryInteractor(
+ authenticationInteractor = authenticationInteractor,
+ sceneInteractor = utils.sceneInteractor(),
)
private val bouncerInteractor =
utils.bouncerInteractor(
+ deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
)
@@ -81,7 +84,7 @@
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
@@ -103,7 +106,7 @@
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
@@ -358,7 +361,7 @@
private fun TestScope.lockDeviceAndOpenPinBouncer() {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
index 5414259..677108c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
@@ -18,10 +18,13 @@
import android.app.ActivityOptions
import android.app.PendingIntent
+import android.content.Context
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.activity.EmptyTestActivity
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -30,11 +33,13 @@
import com.android.wm.shell.taskview.TaskView
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.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.any
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -43,31 +48,31 @@
@TestableLooper.RunWithLooper
class DetailDialogTest : SysuiTestCase() {
- @Mock
- private lateinit var taskView: TaskView
- @Mock
- private lateinit var broadcastSender: BroadcastSender
- @Mock
- private lateinit var controlViewHolder: ControlViewHolder
- @Mock
- private lateinit var pendingIntent: PendingIntent
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var activityStarter: ActivityStarter
+ @Rule
+ @JvmField
+ val activityRule: ActivityScenarioRule<EmptyTestActivity> =
+ ActivityScenarioRule(EmptyTestActivity::class.java)
+
+ @Mock private lateinit var taskView: TaskView
+ @Mock private lateinit var broadcastSender: BroadcastSender
+ @Mock private lateinit var controlViewHolder: ControlViewHolder
+ @Mock private lateinit var pendingIntent: PendingIntent
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+
+ private lateinit var underTest: DetailDialog
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+
+ underTest = createDialog(pendingIntent)
}
@Test
fun testPendingIntentIsUnModified() {
- // GIVEN the dialog is created with a PendingIntent
- val dialog = createDialog(pendingIntent)
-
// WHEN the TaskView is initialized
- dialog.stateCallback.onInitialized()
+ underTest.stateCallback.onInitialized()
// THEN the PendingIntent used to call startActivity is unmodified by systemui
verify(taskView).startActivity(eq(pendingIntent), any(), any(), any())
@@ -75,11 +80,8 @@
@Test
fun testActivityOptionsAllowBal() {
- // GIVEN the dialog is created with a PendingIntent
- val dialog = createDialog(pendingIntent)
-
// WHEN the TaskView is initialized
- dialog.stateCallback.onInitialized()
+ underTest.stateCallback.onInitialized()
val optionsCaptor = argumentCaptor<ActivityOptions>()
@@ -90,17 +92,41 @@
.isEqualTo(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
assertThat(optionsCaptor.value.isPendingIntentBackgroundActivityLaunchAllowedByPermission)
.isTrue()
+ assertThat(optionsCaptor.value.taskAlwaysOnTop).isTrue()
}
- private fun createDialog(pendingIntent: PendingIntent): DetailDialog {
+ @Test
+ fun testDismissRemovesTheTask() {
+ activityRule.scenario.onActivity {
+ underTest = createDialog(pendingIntent, it)
+ underTest.show()
+
+ underTest.dismiss()
+
+ verify(taskView).removeTask()
+ verify(taskView, never()).release()
+ }
+ }
+
+ @Test
+ fun testTaskRemovalReleasesTaskView() {
+ underTest.stateCallback.onTaskRemovalStarted(0)
+
+ verify(taskView).release()
+ }
+
+ private fun createDialog(
+ pendingIntent: PendingIntent,
+ context: Context = mContext,
+ ): DetailDialog {
return DetailDialog(
- mContext,
+ context,
broadcastSender,
taskView,
pendingIntent,
controlViewHolder,
keyguardStateController,
- activityStarter
+ activityStarter,
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
new file mode 100644
index 0000000..8e8cbe4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
@@ -0,0 +1,127 @@
+package com.android.systemui.deviceentry.data.repository
+
+import android.content.pm.UserInfo
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class DeviceEntryRepositoryTest : SysuiTestCase() {
+
+ @Mock private lateinit var lockPatternUtils: LockPatternUtils
+ @Mock private lateinit var keyguardBypassController: KeyguardBypassController
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+
+ private val testUtils = SceneTestUtils(this)
+ private val testScope = testUtils.testScope
+ private val userRepository = FakeUserRepository()
+
+ private lateinit var underTest: DeviceEntryRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ userRepository.setUserInfos(USER_INFOS)
+ runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[0]) }
+
+ underTest =
+ DeviceEntryRepositoryImpl(
+ applicationScope = testScope.backgroundScope,
+ backgroundDispatcher = testUtils.testDispatcher,
+ userRepository = userRepository,
+ lockPatternUtils = lockPatternUtils,
+ keyguardBypassController = keyguardBypassController,
+ keyguardStateController = keyguardStateController,
+ )
+ }
+
+ @Test
+ fun isUnlocked() =
+ testScope.runTest {
+ whenever(keyguardStateController.isUnlocked).thenReturn(false)
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+
+ runCurrent()
+ assertThat(isUnlocked).isFalse()
+
+ val captor = argumentCaptor<KeyguardStateController.Callback>()
+ Mockito.verify(keyguardStateController, Mockito.atLeastOnce())
+ .addCallback(captor.capture())
+
+ whenever(keyguardStateController.isUnlocked).thenReturn(true)
+ captor.value.onUnlockedChanged()
+ runCurrent()
+ assertThat(isUnlocked).isTrue()
+
+ whenever(keyguardStateController.isUnlocked).thenReturn(false)
+ captor.value.onKeyguardShowingChanged()
+ runCurrent()
+ assertThat(isUnlocked).isFalse()
+ }
+
+ @Test
+ fun isInsecureLockscreenEnabled() =
+ testScope.runTest {
+ whenever(lockPatternUtils.isLockScreenDisabled(USER_INFOS[0].id)).thenReturn(false)
+ whenever(lockPatternUtils.isLockScreenDisabled(USER_INFOS[1].id)).thenReturn(true)
+
+ userRepository.setSelectedUserInfo(USER_INFOS[0])
+ assertThat(underTest.isInsecureLockscreenEnabled()).isTrue()
+
+ userRepository.setSelectedUserInfo(USER_INFOS[1])
+ assertThat(underTest.isInsecureLockscreenEnabled()).isFalse()
+ }
+
+ @Test
+ fun isBypassEnabled_disabledInController() =
+ testScope.runTest {
+ whenever(keyguardBypassController.isBypassEnabled).thenAnswer { false }
+ whenever(keyguardBypassController.bypassEnabled).thenAnswer { false }
+ assertThat(underTest.isBypassEnabled()).isFalse()
+ }
+
+ @Test
+ fun isBypassEnabled_enabledInController() =
+ testScope.runTest {
+ whenever(keyguardBypassController.isBypassEnabled).thenAnswer { true }
+ whenever(keyguardBypassController.bypassEnabled).thenAnswer { true }
+ assertThat(underTest.isBypassEnabled()).isTrue()
+ }
+
+ companion object {
+ private val USER_INFOS =
+ listOf(
+ UserInfo(
+ /* id= */ 100,
+ /* name= */ "First user",
+ /* flags= */ 0,
+ ),
+ UserInfo(
+ /* id= */ 101,
+ /* name= */ "Second user",
+ /* flags= */ 0,
+ ),
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
new file mode 100644
index 0000000..55582e1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -0,0 +1,234 @@
+package com.android.systemui.deviceentry.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.SceneModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class DeviceEntryInteractorTest : SysuiTestCase() {
+
+ private val utils = SceneTestUtils(this)
+ private val testScope = utils.testScope
+ private val repository: FakeDeviceEntryRepository = utils.deviceEntryRepository
+ private val sceneInteractor = utils.sceneInteractor()
+ private val authenticationInteractor = utils.authenticationInteractor()
+ private val underTest =
+ utils.deviceEntryInteractor(
+ repository = repository,
+ authenticationInteractor = authenticationInteractor,
+ sceneInteractor = sceneInteractor,
+ )
+
+ @Test
+ fun isUnlocked_whenAuthMethodIsNoneAndLockscreenDisabled_isTrue() =
+ testScope.runTest {
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.deviceEntryRepository.apply {
+ setInsecureLockscreenEnabled(false)
+
+ // Toggle isUnlocked, twice.
+ //
+ // This is done because the underTest.isUnlocked flow doesn't receive values from
+ // just changing the state above; the actual isUnlocked state needs to change to
+ // cause the logic under test to "pick up" the current state again.
+ //
+ // It is done twice to make sure that we don't actually change the isUnlocked state
+ // from what it originally was.
+ setUnlocked(!isUnlocked.value)
+ runCurrent()
+ setUnlocked(!isUnlocked.value)
+ runCurrent()
+ }
+
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+ assertThat(isUnlocked).isTrue()
+ }
+
+ @Test
+ fun isUnlocked_whenAuthMethodIsNoneAndLockscreenEnabled_isTrue() =
+ testScope.runTest {
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+ assertThat(isUnlocked).isTrue()
+ }
+
+ @Test
+ fun isDeviceEntered_onLockscreenWithSwipe_isFalse() =
+ testScope.runTest {
+ val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ switchToScene(SceneKey.Lockscreen)
+
+ assertThat(isDeviceEntered).isFalse()
+ }
+
+ @Test
+ fun isDeviceEntered_onShadeBeforeDismissingLockscreenWithSwipe_isFalse() =
+ testScope.runTest {
+ val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ switchToScene(SceneKey.Lockscreen)
+ runCurrent()
+ switchToScene(SceneKey.Shade)
+
+ assertThat(isDeviceEntered).isFalse()
+ }
+
+ @Test
+ fun isDeviceEntered_afterDismissingLockscreenWithSwipe_isTrue() =
+ testScope.runTest {
+ val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ switchToScene(SceneKey.Lockscreen)
+ runCurrent()
+ switchToScene(SceneKey.Gone)
+
+ assertThat(isDeviceEntered).isTrue()
+ }
+
+ @Test
+ fun isDeviceEntered_onShadeAfterDismissingLockscreenWithSwipe_isTrue() =
+ testScope.runTest {
+ val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ switchToScene(SceneKey.Lockscreen)
+ runCurrent()
+ switchToScene(SceneKey.Gone)
+ runCurrent()
+ switchToScene(SceneKey.Shade)
+
+ assertThat(isDeviceEntered).isTrue()
+ }
+
+ @Test
+ fun isDeviceEntered_onBouncer_isFalse() =
+ testScope.runTest {
+ utils.authenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pattern
+ )
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ switchToScene(SceneKey.Lockscreen)
+ runCurrent()
+ switchToScene(SceneKey.Bouncer)
+
+ val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
+ assertThat(isDeviceEntered).isFalse()
+ }
+
+ @Test
+ fun canSwipeToEnter_onLockscreenWithSwipe_isTrue() =
+ testScope.runTest {
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ switchToScene(SceneKey.Lockscreen)
+
+ val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter)
+ assertThat(canSwipeToEnter).isTrue()
+ }
+
+ @Test
+ fun canSwipeToEnter_onLockscreenWithPin_isFalse() =
+ testScope.runTest {
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ switchToScene(SceneKey.Lockscreen)
+
+ val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter)
+ assertThat(canSwipeToEnter).isFalse()
+ }
+
+ @Test
+ fun canSwipeToEnter_afterLockscreenDismissedInSwipeMode_isFalse() =
+ testScope.runTest {
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ switchToScene(SceneKey.Lockscreen)
+ runCurrent()
+ switchToScene(SceneKey.Gone)
+
+ val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter)
+ assertThat(canSwipeToEnter).isFalse()
+ }
+
+ @Test
+ fun isAuthenticationRequired_lockedAndSecured_true() =
+ testScope.runTest {
+ utils.deviceEntryRepository.setUnlocked(false)
+ runCurrent()
+ utils.authenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Password
+ )
+
+ assertThat(underTest.isAuthenticationRequired()).isTrue()
+ }
+
+ @Test
+ fun isAuthenticationRequired_lockedAndNotSecured_false() =
+ testScope.runTest {
+ utils.deviceEntryRepository.setUnlocked(false)
+ runCurrent()
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+
+ assertThat(underTest.isAuthenticationRequired()).isFalse()
+ }
+
+ @Test
+ fun isAuthenticationRequired_unlockedAndSecured_false() =
+ testScope.runTest {
+ utils.deviceEntryRepository.setUnlocked(true)
+ runCurrent()
+ utils.authenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Password
+ )
+
+ assertThat(underTest.isAuthenticationRequired()).isFalse()
+ }
+
+ @Test
+ fun isAuthenticationRequired_unlockedAndNotSecured_false() =
+ testScope.runTest {
+ utils.deviceEntryRepository.setUnlocked(true)
+ runCurrent()
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+
+ assertThat(underTest.isAuthenticationRequired()).isFalse()
+ }
+
+ @Test
+ fun isBypassEnabled_enabledInRepository_true() =
+ testScope.runTest {
+ utils.deviceEntryRepository.setBypassEnabled(true)
+ assertThat(underTest.isBypassEnabled()).isTrue()
+ }
+
+ @Test
+ fun isBypassEnabled_disabledInRepository_false() =
+ testScope.runTest {
+ utils.deviceEntryRepository.setBypassEnabled(false)
+ assertThat(underTest.isBypassEnabled()).isFalse()
+ }
+
+ private fun switchToScene(sceneKey: SceneKey) {
+ sceneInteractor.changeScene(SceneModel(sceneKey), "reason")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
index add601c..8d12e49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
@@ -158,8 +158,8 @@
var progress = 0.5f
sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, progress))
- // GIVEN a progress event due to a touch on the slider track at threshold
- progress += config.jumpThreshold
+ // GIVEN a progress event due to a touch on the slider track beyond threshold
+ progress += (config.jumpThreshold + 0.01f)
sliderEventProducer.sendEvent(
SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, progress)
)
@@ -191,7 +191,6 @@
// THEN the tracker moves to the jump-track location selected state
assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.JUMP_BOOKEND_SELECTED)
assertThat(mSeekableSliderTracker.isWaiting).isFalse()
- verify(sliderStateListener).onUpperBookend()
verifyNoMoreInteractions(sliderStateListener)
}
@@ -214,7 +213,6 @@
// THEN the tracker moves to the JUMP_TRACK_LOCATION_SELECTED state
assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.JUMP_BOOKEND_SELECTED)
assertThat(mSeekableSliderTracker.isWaiting).isFalse()
- verify(sliderStateListener).onLowerBookend()
verifyNoMoreInteractions(sliderStateListener)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index 9a2936e..7a13a0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -102,7 +102,7 @@
notificationShadeWindowController, powerManager, wallpaperManager
)
keyguardUnlockAnimationController.setLauncherUnlockController(
- launcherUnlockAnimationController)
+ "", launcherUnlockAnimationController)
whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
whenever(powerManager.isInteractive).thenReturn(true)
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 9d96ac7..291dda20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -104,6 +104,7 @@
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.ShadeWindowLogger;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -185,6 +186,7 @@
private @Mock SysuiColorExtractor mColorExtractor;
private @Mock AuthController mAuthController;
private @Mock ShadeExpansionStateManager mShadeExpansionStateManager;
+ private @Mock ShadeInteractor mShadeInteractor;
private @Mock ShadeWindowLogger mShadeWindowLogger;
private @Captor ArgumentCaptor<KeyguardStateController.Callback>
mKeyguardStateControllerCallback;
@@ -249,6 +251,7 @@
mScreenOffAnimationController,
mAuthController,
mShadeExpansionStateManager,
+ () -> mShadeInteractor,
mShadeWindowLogger);
mFeatureFlags = new FakeFeatureFlags();
mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 2691860..f93051c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -44,7 +44,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -81,7 +80,6 @@
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var dozeTransitionListener: DozeTransitionListener
@Mock private lateinit var authController: AuthController
- @Mock private lateinit var keyguardBypassController: KeyguardBypassController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var dreamOverlayCallbackController: DreamOverlayCallbackController
@Mock private lateinit var dozeParameters: DozeParameters
@@ -103,7 +101,6 @@
screenLifecycle,
biometricUnlockController,
keyguardStateController,
- keyguardBypassController,
keyguardUpdateMonitor,
dozeTransitionListener,
dozeParameters,
@@ -213,23 +210,9 @@
}
@Test
- fun isBypassEnabled_disabledInController() {
- whenever(keyguardBypassController.isBypassEnabled).thenReturn(false)
- whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
- assertThat(underTest.isBypassEnabled()).isFalse()
- }
-
- @Test
- fun isBypassEnabled_enabledInController() {
- whenever(keyguardBypassController.isBypassEnabled).thenReturn(true)
- whenever(keyguardBypassController.bypassEnabled).thenReturn(true)
- assertThat(underTest.isBypassEnabled()).isTrue()
- }
-
- @Test
fun isAodAvailable() = runTest {
val flow = underTest.isAodAvailable
- var isAodAvailable = collectLastValue(flow)
+ val isAodAvailable = collectLastValue(flow)
runCurrent()
val callback =
@@ -273,29 +256,6 @@
}
@Test
- fun isKeyguardUnlocked() =
- testScope.runTest {
- whenever(keyguardStateController.isUnlocked).thenReturn(false)
- val isKeyguardUnlocked by collectLastValue(underTest.isKeyguardUnlocked)
-
- runCurrent()
- assertThat(isKeyguardUnlocked).isFalse()
-
- val captor = argumentCaptor<KeyguardStateController.Callback>()
- verify(keyguardStateController, atLeastOnce()).addCallback(captor.capture())
-
- whenever(keyguardStateController.isUnlocked).thenReturn(true)
- captor.value.onUnlockedChanged()
- runCurrent()
- assertThat(isKeyguardUnlocked).isTrue()
-
- whenever(keyguardStateController.isUnlocked).thenReturn(false)
- captor.value.onKeyguardShowingChanged()
- runCurrent()
- assertThat(isKeyguardUnlocked).isFalse()
- }
-
- @Test
fun isDozing() =
testScope.runTest {
underTest.setIsDozing(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 90e217f..82c7fa4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -15,8 +15,6 @@
*
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
package com.android.systemui.keyguard.domain.interactor
import android.app.StatusBarManager
@@ -27,13 +25,11 @@
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.shade.data.repository.FakeShadeRepository
@@ -42,62 +38,53 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.onCompletion
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class KeyguardInteractorTest : SysuiTestCase() {
- private lateinit var commandQueue: FakeCommandQueue
- private lateinit var featureFlags: FakeFeatureFlags
- private lateinit var testScope: TestScope
- private lateinit var underTest: KeyguardInteractor
- private lateinit var repository: FakeKeyguardRepository
- private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
- private lateinit var configurationRepository: FakeConfigurationRepository
- private lateinit var shadeRepository: FakeShadeRepository
- private lateinit var sceneInteractor: SceneInteractor
- private lateinit var transitionState: MutableStateFlow<ObservableTransitionState>
+ private val testUtils = SceneTestUtils(this)
+ private val testScope = testUtils.testScope
+ private val repository = testUtils.keyguardRepository
+ private val sceneInteractor = testUtils.sceneInteractor()
+ private val commandQueue = FakeCommandQueue()
+ private val featureFlags = FakeFeatureFlagsClassic().apply { set(FACE_AUTH_REFACTOR, true) }
+ private val bouncerRepository = FakeKeyguardBouncerRepository()
+ private val configurationRepository = FakeConfigurationRepository()
+ private val shadeRepository = FakeShadeRepository()
+ private val transitionState: MutableStateFlow<ObservableTransitionState> =
+ MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Gone))
+
+ private val underTest =
+ KeyguardInteractor(
+ repository = repository,
+ commandQueue = commandQueue,
+ featureFlags = featureFlags,
+ sceneContainerFlags = testUtils.sceneContainerFlags,
+ deviceEntryRepository = testUtils.deviceEntryRepository,
+ bouncerRepository = bouncerRepository,
+ configurationRepository = configurationRepository,
+ shadeRepository = shadeRepository,
+ sceneInteractorProvider = { sceneInteractor },
+ )
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
- featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
- commandQueue = FakeCommandQueue()
- val sceneTestUtils = SceneTestUtils(this)
- testScope = sceneTestUtils.testScope
- repository = sceneTestUtils.keyguardRepository
- bouncerRepository = FakeKeyguardBouncerRepository()
- configurationRepository = FakeConfigurationRepository()
- shadeRepository = FakeShadeRepository()
- sceneInteractor = sceneTestUtils.sceneInteractor()
- transitionState = MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Gone))
sceneInteractor.setTransitionState(transitionState)
- underTest =
- KeyguardInteractor(
- repository = repository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- sceneContainerFlags = sceneTestUtils.sceneContainerFlags,
- bouncerRepository = bouncerRepository,
- configurationRepository = configurationRepository,
- shadeRepository = shadeRepository,
- sceneInteractorProvider = { sceneInteractor },
- )
}
@Test
fun onCameraLaunchDetected() =
testScope.runTest {
val flow = underTest.onCameraLaunchDetected
- var cameraLaunchSource = collectLastValue(flow)
+ val cameraLaunchSource = collectLastValue(flow)
runCurrent()
commandQueue.doForEachCallback {
@@ -175,7 +162,7 @@
@Test
fun keyguardVisibilityIsDefinedAsKeyguardShowingButNotOccluded() = runTest {
- var isVisible = collectLastValue(underTest.isKeyguardVisible)
+ val isVisible = collectLastValue(underTest.isKeyguardVisible)
repository.setKeyguardShowing(true)
repository.setKeyguardOccluded(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index f2636c5..591653e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -1226,7 +1226,6 @@
// GIVEN the keyguard is showing locked
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
- keyguardRepository.setKeyguardUnlocked(false)
runCurrent()
shadeRepository.setShadeModel(
ShadeModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 1681cfd..6e94691 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -37,10 +37,6 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
- private val authenticationInteractor =
- utils.authenticationInteractor(
- repository = utils.authenticationRepository(),
- )
private val underTest = createLockscreenSceneViewModel()
@@ -49,8 +45,8 @@
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.authenticationRepository.setLockscreenEnabled(true)
- utils.authenticationRepository.setUnlocked(true)
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ utils.deviceEntryRepository.setUnlocked(true)
sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone)
@@ -61,7 +57,7 @@
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
@@ -88,7 +84,11 @@
private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
return LockscreenSceneViewModel(
applicationScope = testScope.backgroundScope,
- authenticationInteractor = authenticationInteractor,
+ deviceEntryInteractor =
+ utils.deviceEntryInteractor(
+ authenticationInteractor = utils.authenticationInteractor(),
+ sceneInteractor = utils.sceneInteractor(),
+ ),
communalInteractor = utils.communalInteractor(),
longPress =
KeyguardLongPressViewModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
index 25faeef..de57b60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
@@ -81,6 +81,7 @@
@Mock private lateinit var mediaDataManager: MediaDataManager
@Mock private lateinit var uniqueObjectHostView: UniqueObjectHostView
@Mock private lateinit var dreamOverlayStateController: DreamOverlayStateController
+ @Mock lateinit var logger: MediaViewLogger
@Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@Captor
@@ -121,7 +122,8 @@
notifPanelEvents,
settings,
fakeHandler,
- ResourcesSplitShadeStateController()
+ ResourcesSplitShadeStateController(),
+ logger,
)
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
verify(statusBarStateController).addCallback(statusBarCallback.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index b595e8d..79411f42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -66,6 +66,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.di.NewQSTileFactory;
import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
@@ -77,6 +78,8 @@
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
+import dagger.Lazy;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -102,8 +105,6 @@
private static final String SETTING = QSHost.TILES_SETTING;
@Mock
- private QSFactory mDefaultFactory;
- @Mock
private PluginManager mPluginManager;
@Mock
private TunerService mTunerService;
@@ -117,7 +118,6 @@
private CustomTile mCustomTile;
@Mock
private UserTracker mUserTracker;
- private SecureSettings mSecureSettings;
@Mock
private CustomTileStatePersister mCustomTileStatePersister;
@Mock
@@ -127,6 +127,10 @@
@Mock
private UserFileManager mUserFileManager;
+ private SecureSettings mSecureSettings;
+
+ private QSFactory mDefaultFactory;
+
private SparseArray<SharedPreferences> mSharedPreferencesByUser;
private FakeFeatureFlags mFeatureFlags;
@@ -144,6 +148,8 @@
mFeatureFlags.set(Flags.QS_PIPELINE_NEW_HOST, false);
mFeatureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, false);
+ // TODO(b/299909337): Add test checking the new factory is used when the flag is on
+ mFeatureFlags.set(Flags.QS_PIPELINE_NEW_TILES, false);
mQSPipelineFlagsRepository = new QSPipelineFlagsRepository(mFeatureFlags);
mMainExecutor = new FakeExecutor(new FakeSystemClock());
@@ -164,7 +170,8 @@
mSecureSettings = new FakeSettings();
saveSetting("");
- mQSTileHost = new TestQSTileHost(mContext, mDefaultFactory, mMainExecutor,
+ setUpTileFactory();
+ mQSTileHost = new TestQSTileHost(mContext, () -> null, mDefaultFactory, mMainExecutor,
mPluginManager, mTunerService, () -> mAutoTiles, mShadeController,
mQSLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister,
mTileLifecycleManagerFactory, mUserFileManager, mQSPipelineFlagsRepository);
@@ -178,7 +185,6 @@
mMainExecutor.runAllReady();
}
}, mUserTracker.getUserId());
- setUpTileFactory();
}
private void saveSetting(String value) {
@@ -191,32 +197,29 @@
}
private void setUpTileFactory() {
- // Only create this kind of tiles
- when(mDefaultFactory.createTile(anyString())).thenAnswer(
- invocation -> {
- String spec = invocation.getArgument(0);
- if ("spec1".equals(spec)) {
- return new TestTile1(mQSTileHost);
- } else if ("spec2".equals(spec)) {
- return new TestTile2(mQSTileHost);
- } else if ("spec3".equals(spec)) {
- return new TestTile3(mQSTileHost);
- } else if ("na".equals(spec)) {
- return new NotAvailableTile(mQSTileHost);
- } else if (CUSTOM_TILE_SPEC.equals(spec)) {
- QSTile tile = mCustomTile;
- QSTile.State s = mock(QSTile.State.class);
- s.spec = spec;
- when(mCustomTile.getState()).thenReturn(s);
- return tile;
- } else if ("internet".equals(spec)
- || "wifi".equals(spec)
- || "cell".equals(spec)) {
- return new TestTile1(mQSTileHost);
- } else {
- return null;
- }
- });
+ mDefaultFactory = new FakeQSFactory(spec -> {
+ if ("spec1".equals(spec)) {
+ return new TestTile1(mQSTileHost);
+ } else if ("spec2".equals(spec)) {
+ return new TestTile2(mQSTileHost);
+ } else if ("spec3".equals(spec)) {
+ return new TestTile3(mQSTileHost);
+ } else if ("na".equals(spec)) {
+ return new NotAvailableTile(mQSTileHost);
+ } else if (CUSTOM_TILE_SPEC.equals(spec)) {
+ QSTile tile = mCustomTile;
+ QSTile.State s = mock(QSTile.State.class);
+ s.spec = spec;
+ when(mCustomTile.getState()).thenReturn(s);
+ return tile;
+ } else if ("internet".equals(spec)
+ || "wifi".equals(spec)
+ || "cell".equals(spec)) {
+ return new TestTile1(mQSTileHost);
+ } else {
+ return null;
+ }
+ });
when(mCustomTile.isAvailable()).thenReturn(true);
}
@@ -703,7 +706,7 @@
}
private class TestQSTileHost extends QSTileHost {
- TestQSTileHost(Context context,
+ TestQSTileHost(Context context, Lazy<NewQSTileFactory> newQSTileFactoryProvider,
QSFactory defaultFactory, Executor mainExecutor,
PluginManager pluginManager, TunerService tunerService,
Provider<AutoTileManager> autoTiles,
@@ -712,7 +715,7 @@
CustomTileStatePersister customTileStatePersister,
TileLifecycleManager.Factory tileLifecycleManagerFactory,
UserFileManager userFileManager, QSPipelineFlagsRepository featureFlags) {
- super(context, defaultFactory, mainExecutor, pluginManager,
+ super(context, newQSTileFactoryProvider, defaultFactory, mainExecutor, pluginManager,
tunerService, autoTiles, shadeController, qsLogger,
userTracker, secureSettings, customTileStatePersister,
tileLifecycleManagerFactory, userFileManager, featureFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index bde3038..d3cd26b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -124,6 +124,7 @@
if (FACTORY_TILES.contains(spec)) {
FakeQSTile tile = new FakeQSTile(mBgExecutor, mMainExecutor);
tile.setState(mState);
+ tile.setTileSpec(spec);
return tile;
} else {
return null;
@@ -284,7 +285,10 @@
Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, null);
QSTile t = mock(QSTile.class);
- when(mQSHost.createTile("hotspot")).thenReturn(t);
+ when(mQSHost.createTile("hotspot")).thenAnswer(invocation -> {
+ t.setTileSpec("hotspot");
+ return t;
+ });
mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
"hotspot");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 126dd63..43cf1b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -16,12 +16,15 @@
package com.android.systemui.qs.external
+import android.app.IUriGrantsManager
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
+import android.graphics.Bitmap
+import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.os.Handler
@@ -49,6 +52,7 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
@@ -67,6 +71,8 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import java.util.Arrays
+
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -74,10 +80,8 @@
class CustomTileTest : SysuiTestCase() {
companion object {
- const val packageName = "test_package"
const val className = "test_class"
- val componentName = ComponentName(packageName, className)
- val TILE_SPEC = CustomTile.toSpec(componentName)
+ val UID = 12345
}
@Mock private lateinit var tileHost: QSHost
@@ -94,11 +98,36 @@
@Mock private lateinit var serviceInfo: ServiceInfo
@Mock private lateinit var customTileStatePersister: CustomTileStatePersister
@Mock private lateinit var uiEventLogger: QsEventLogger
+ @Mock private lateinit var ugm: IUriGrantsManager
private var displayTracker = FakeDisplayTracker(mContext)
private lateinit var customTile: CustomTile
private lateinit var testableLooper: TestableLooper
- private lateinit var customTileBuilder: CustomTile.Builder
+ private val packageName = context.packageName
+ private val componentName = ComponentName(packageName, className)
+ private val TILE_SPEC = CustomTile.toSpec(componentName)
+
+ private val customTileFactory = object : CustomTile.Factory {
+ override fun create(action: String, userContext: Context): CustomTile {
+ return CustomTile(
+ { tileHost },
+ uiEventLogger,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ FalsingManagerFake(),
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ action,
+ userContext,
+ customTileStatePersister,
+ tileServices,
+ displayTracker,
+ ugm,
+ )
+ }
+ }
@Before
fun setUp() {
@@ -116,24 +145,13 @@
`when`(packageManager.getServiceInfo(any(ComponentName::class.java), anyInt()))
.thenReturn(serviceInfo)
+ `when`(packageManager.getResourcesForApplication(any<ApplicationInfo>()))
+ .thenReturn(context.resources)
+
serviceInfo.applicationInfo = applicationInfo
- customTileBuilder = CustomTile.Builder(
- { tileHost },
- uiEventLogger,
- testableLooper.looper,
- Handler(testableLooper.looper),
- FalsingManagerFake(),
- metricsLogger,
- statusBarStateController,
- activityStarter,
- qsLogger,
- customTileStatePersister,
- tileServices,
- displayTracker
- )
- customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ customTile = CustomTile.create(customTileFactory, TILE_SPEC, mContext)
customTile.initialize()
testableLooper.processAllMessages()
}
@@ -146,7 +164,7 @@
`when`(userContext.packageManager).thenReturn(packageManager)
`when`(userContext.userId).thenReturn(10)
- val tile = CustomTile.create(customTileBuilder, TILE_SPEC, userContext)
+ val tile = CustomTile.create(customTileFactory, TILE_SPEC, userContext)
tile.initialize()
testableLooper.processAllMessages()
@@ -156,7 +174,7 @@
@Test
fun testToggleableTileHasBooleanState() {
`when`(tileServiceManager.isToggleableTile).thenReturn(true)
- customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ customTile = CustomTile.create(customTileFactory, TILE_SPEC, mContext)
customTile.initialize()
testableLooper.processAllMessages()
@@ -173,7 +191,7 @@
@Test
fun testValueUpdatedInBooleanTile() {
`when`(tileServiceManager.isToggleableTile).thenReturn(true)
- customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ customTile = CustomTile.create(customTileFactory, TILE_SPEC, mContext)
customTile.initialize()
testableLooper.processAllMessages()
@@ -219,7 +237,7 @@
val t = Tile().apply {
state = Tile.STATE_INACTIVE
}
- customTile.updateTileState(t)
+ customTile.updateTileState(t, UID)
testableLooper.processAllMessages()
verify(customTileStatePersister, never()).persistState(any(), any())
@@ -243,7 +261,7 @@
`when`(tileServiceManager.isActiveTile).thenReturn(true)
`when`(customTileStatePersister
.readState(TileServiceKey(componentName, customTile.user))).thenReturn(t)
- val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ val tile = CustomTile.create(customTileFactory, TILE_SPEC, mContext)
tile.initialize()
testableLooper.processAllMessages()
@@ -281,11 +299,11 @@
}
`when`(tileServiceManager.isActiveTile).thenReturn(true)
- val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ val tile = CustomTile.create(customTileFactory, TILE_SPEC, mContext)
tile.initialize()
testableLooper.processAllMessages()
- tile.updateTileState(t)
+ tile.updateTileState(t, UID)
testableLooper.processAllMessages()
@@ -297,13 +315,13 @@
fun testAvailableBeforeInitialization() {
`when`(packageManager.getApplicationInfo(anyString(), anyInt()))
.thenThrow(PackageManager.NameNotFoundException())
- val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ val tile = CustomTile.create(customTileFactory, TILE_SPEC, mContext)
assertTrue(tile.isAvailable)
}
@Test
fun testNotAvailableAfterInitializationWithoutIcon() {
- val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ val tile = CustomTile.create(customTileFactory, TILE_SPEC, mContext)
reset(tileHost)
tile.initialize()
testableLooper.processAllMessages()
@@ -315,7 +333,7 @@
fun testInvalidPendingIntentDoesNotStartActivity() {
val pi = mock(PendingIntent::class.java)
`when`(pi.isActivity).thenReturn(false)
- val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ val tile = CustomTile.create(customTileFactory, TILE_SPEC, mContext)
assertThrows(IllegalArgumentException::class.java) {
tile.qsTile.activityLaunchForClick = pi
@@ -333,7 +351,7 @@
fun testValidPendingIntentWithNoClickDoesNotStartActivity() {
val pi = mock(PendingIntent::class.java)
`when`(pi.isActivity).thenReturn(true)
- val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ val tile = CustomTile.create(customTileFactory, TILE_SPEC, mContext)
tile.qsTile.activityLaunchForClick = pi
testableLooper.processAllMessages()
@@ -347,7 +365,7 @@
fun testValidPendingIntentStartsActivity() {
val pi = mock(PendingIntent::class.java)
`when`(pi.isActivity).thenReturn(true)
- val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ val tile = CustomTile.create(customTileFactory, TILE_SPEC, mContext)
tile.qsTile.activityLaunchForClick = pi
tile.handleClick(mock(LaunchableFrameLayout::class.java))
@@ -363,7 +381,7 @@
fun testActiveTileListensOnceAfterCreated() {
`when`(tileServiceManager.isActiveTile).thenReturn(true)
- val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ val tile = CustomTile.create(customTileFactory, TILE_SPEC, mContext)
tile.initialize()
tile.postStale()
testableLooper.processAllMessages()
@@ -376,7 +394,7 @@
fun testActiveTileDoesntListenAfterFirstTime() {
`when`(tileServiceManager.isActiveTile).thenReturn(true)
- val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ val tile = CustomTile.create(customTileFactory, TILE_SPEC, mContext)
tile.initialize()
// Make sure we have an icon in the tile because we don't have a default icon
// This should not be overridden by the retrieved tile that has null icon.
@@ -424,19 +442,128 @@
// Set the tile to listening and apply the tile (unmodified)
customTile.handleSetListening(true)
testableLooper.processAllMessages()
- customTile.updateTileState(tile)
+ customTile.updateTileState(tile, UID)
customTile.refreshState()
testableLooper.processAllMessages()
assertThat(customTile.state.label).isEqualTo(label2)
}
- private fun copyTileUsingParcel(t: Tile): Tile {
- val parcel = Parcel.obtain()
- parcel.setDataPosition(0)
- t.writeToParcel(parcel, 0)
- parcel.setDataPosition(0)
+ @Test
+ fun uriIconLoadSuccess_correctIcon() {
+ val size = 100
+ val icon = mock(Icon::class.java)
+ val drawable = context.getDrawable(R.drawable.cloud)!!
+ whenever(icon.loadDrawable(any())).thenReturn(drawable)
+ whenever(icon.loadDrawableCheckingUriGrant(
+ any(),
+ eq(ugm),
+ anyInt(),
+ anyString())
+ ).thenReturn(drawable)
- return Tile.CREATOR.createFromParcel(parcel)
+ serviceInfo.icon = R.drawable.android
+
+ customTile.handleSetListening(true)
+ testableLooper.processAllMessages()
+ customTile.handleSetListening(false)
+ testableLooper.processAllMessages()
+
+ val tile = copyTileUsingParcel(customTile.qsTile)
+ tile.icon = icon
+
+ customTile.updateTileState(tile, UID)
+
+ customTile.refreshState()
+ testableLooper.processAllMessages()
+
+ verify(icon).loadDrawableCheckingUriGrant(context, ugm, UID, packageName)
+
+ assertThat(
+ areDrawablesEqual(
+ customTile.state.iconSupplier.get().getDrawable(context),
+ drawable,
+ size
+ )
+ ).isTrue()
}
+
+ @Test
+ fun uriIconLoadFailsWithoutGrant_defaultIcon() {
+ val size = 100
+ val drawable = context.getDrawable(R.drawable.cloud)!!
+ val icon = mock(Icon::class.java)
+ whenever(icon.loadDrawable(any())).thenReturn(drawable)
+ whenever(icon.loadDrawableCheckingUriGrant(
+ any(),
+ eq(ugm),
+ anyInt(),
+ anyString())
+ ).thenReturn(null)
+
+ // Give it an icon to prevent issues
+ serviceInfo.icon = R.drawable.android
+
+ customTile.handleSetListening(true)
+ testableLooper.processAllMessages()
+ customTile.handleSetListening(false)
+ testableLooper.processAllMessages()
+
+ val tile = copyTileUsingParcel(customTile.qsTile)
+ tile.icon = icon
+
+ customTile.updateTileState(tile, UID)
+
+ customTile.refreshState()
+ testableLooper.processAllMessages()
+
+ verify(icon).loadDrawableCheckingUriGrant(context, ugm, UID, packageName)
+
+ assertThat(
+ areDrawablesEqual(
+ customTile.state.iconSupplier.get().getDrawable(context),
+ context.getDrawable(R.drawable.android)!!,
+ size
+ )
+ ).isTrue()
+ }
+}
+
+private fun areDrawablesEqual(drawable1: Drawable, drawable2: Drawable, size: Int = 24): Boolean {
+ val bm1 = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
+ val bm2 = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
+
+ val canvas1 = Canvas(bm1)
+ val canvas2 = Canvas(bm2)
+
+ drawable1.setBounds(0, 0, size, size)
+ drawable2.setBounds(0, 0, size, size)
+
+ drawable1.draw(canvas1)
+ drawable2.draw(canvas2)
+
+ return equalBitmaps(bm1, bm2).also {
+ bm1.recycle()
+ bm2.recycle()
+ }
+}
+
+private fun equalBitmaps(a: Bitmap, b: Bitmap): Boolean {
+ if (a.width != b.width || a.height != b.height) return false
+ val w = a.width
+ val h = a.height
+ val aPix = IntArray(w * h)
+ val bPix = IntArray(w * h)
+ a.getPixels(aPix, 0, w, 0, 0, w, h)
+ b.getPixels(bPix, 0, w, 0, 0, w, h)
+ return Arrays.equals(aPix, bPix)
+}
+
+private fun copyTileUsingParcel(t: Tile): Tile {
+ val parcel = Parcel.obtain()
+ parcel.setDataPosition(0)
+ t.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+
+ return Tile.CREATOR.createFromParcel(parcel)
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
index 365e8a5..78c2acf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
@@ -16,6 +16,11 @@
package com.android.systemui.qs.external
+import android.app.IUriGrantsManager
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -26,11 +31,21 @@
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.qs.QSTileView
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.Arrays
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -40,12 +55,19 @@
companion object {
private const val APP_NAME = "App name"
private const val LABEL = "Label"
+ private const val PACKAGE = "package"
+ private const val UID = 12345
+ private val DEFAULT_ICON = R.drawable.android
}
private lateinit var dialog: TileRequestDialog
+ @Mock
+ private lateinit var ugm: IUriGrantsManager
+
@Before
fun setUp() {
+ MockitoAnnotations.initMocks(this)
// Create in looper so we can make sure that the tile is fully updated
TestableLooper.get(this).runWithLooper {
dialog = TileRequestDialog(mContext)
@@ -62,9 +84,9 @@
@Test
fun setTileData_hasCorrectViews() {
val icon = Icon.createWithResource(mContext, R.drawable.cloud)
- val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+ val tileData = TileRequestDialog.TileData(UID, APP_NAME, LABEL, icon, PACKAGE)
- dialog.setTileData(tileData)
+ dialog.setTileData(tileData, ugm)
dialog.show()
val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
@@ -77,9 +99,9 @@
@Test
fun setTileData_hasCorrectAppName() {
val icon = Icon.createWithResource(mContext, R.drawable.cloud)
- val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+ val tileData = TileRequestDialog.TileData(UID, APP_NAME, LABEL, icon, PACKAGE)
- dialog.setTileData(tileData)
+ dialog.setTileData(tileData, ugm)
dialog.show()
val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
@@ -90,9 +112,9 @@
@Test
fun setTileData_hasCorrectLabel() {
val icon = Icon.createWithResource(mContext, R.drawable.cloud)
- val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+ val tileData = TileRequestDialog.TileData(UID, APP_NAME, LABEL, icon, PACKAGE)
- dialog.setTileData(tileData)
+ dialog.setTileData(tileData, ugm)
dialog.show()
TestableLooper.get(this).processAllMessages()
@@ -105,9 +127,9 @@
@Test
fun setTileData_hasIcon() {
val icon = Icon.createWithResource(mContext, R.drawable.cloud)
- val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+ val tileData = TileRequestDialog.TileData(UID, APP_NAME, LABEL, icon, PACKAGE)
- dialog.setTileData(tileData)
+ dialog.setTileData(tileData, ugm)
dialog.show()
TestableLooper.get(this).processAllMessages()
@@ -119,9 +141,9 @@
@Test
fun setTileData_nullIcon_hasIcon() {
- val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, null)
+ val tileData = TileRequestDialog.TileData(UID, APP_NAME, LABEL, null, PACKAGE)
- dialog.setTileData(tileData)
+ dialog.setTileData(tileData, ugm)
dialog.show()
TestableLooper.get(this).processAllMessages()
@@ -134,9 +156,9 @@
@Test
fun setTileData_hasNoStateDescription() {
val icon = Icon.createWithResource(mContext, R.drawable.cloud)
- val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+ val tileData = TileRequestDialog.TileData(UID, APP_NAME, LABEL, icon, PACKAGE)
- dialog.setTileData(tileData)
+ dialog.setTileData(tileData, ugm)
dialog.show()
TestableLooper.get(this).processAllMessages()
@@ -150,9 +172,9 @@
@Test
fun setTileData_tileNotClickable() {
val icon = Icon.createWithResource(mContext, R.drawable.cloud)
- val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+ val tileData = TileRequestDialog.TileData(UID, APP_NAME, LABEL, icon, PACKAGE)
- dialog.setTileData(tileData)
+ dialog.setTileData(tileData, ugm)
dialog.show()
TestableLooper.get(this).processAllMessages()
@@ -167,9 +189,9 @@
@Test
fun setTileData_tileHasCorrectContentDescription() {
val icon = Icon.createWithResource(mContext, R.drawable.cloud)
- val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+ val tileData = TileRequestDialog.TileData(UID, APP_NAME, LABEL, icon, PACKAGE)
- dialog.setTileData(tileData)
+ dialog.setTileData(tileData, ugm)
dialog.show()
TestableLooper.get(this).processAllMessages()
@@ -179,4 +201,111 @@
assertThat(tile.contentDescription).isEqualTo(LABEL)
}
+
+ @Test
+ fun uriIconLoadSuccess_correctIcon() {
+ val tintColor = Color.BLACK
+ val icon = Mockito.mock(Icon::class.java)
+ val drawable = context.getDrawable(R.drawable.cloud)!!.apply {
+ setTint(tintColor)
+ }
+ whenever(icon.loadDrawable(any())).thenReturn(drawable)
+ whenever(icon.loadDrawableCheckingUriGrant(
+ any(),
+ eq(ugm),
+ anyInt(),
+ anyString())
+ ).thenReturn(drawable)
+
+ val size = 100
+
+ val tileData = TileRequestDialog.TileData(UID, APP_NAME, LABEL, icon, PACKAGE)
+
+ dialog.setTileData(tileData, ugm)
+ dialog.show()
+
+ TestableLooper.get(this).processAllMessages()
+
+ verify(icon).loadDrawableCheckingUriGrant(any(), eq(ugm), eq(UID), eq(PACKAGE))
+
+ val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
+ val tile = content.getChildAt(1) as QSTileView
+
+ val iconDrawable = (tile.icon.iconView as ImageView).drawable.apply {
+ setTint(tintColor)
+ }
+
+ assertThat(areDrawablesEqual(iconDrawable, drawable, size)).isTrue()
+ }
+
+ @Test
+ fun uriIconLoadFail_defaultIcon() {
+ val tintColor = Color.BLACK
+ val icon = Mockito.mock(Icon::class.java)
+ val drawable = context.getDrawable(R.drawable.cloud)!!.apply {
+ setTint(tintColor)
+ }
+ whenever(icon.loadDrawable(any())).thenReturn(drawable)
+ whenever(icon.loadDrawableCheckingUriGrant(
+ any(),
+ eq(ugm),
+ anyInt(),
+ anyString())
+ ).thenReturn(null)
+
+ val size = 100
+
+ val tileData = TileRequestDialog.TileData(UID, APP_NAME, LABEL, icon, PACKAGE)
+
+ dialog.setTileData(tileData, ugm)
+ dialog.show()
+
+ TestableLooper.get(this).processAllMessages()
+
+ verify(icon).loadDrawableCheckingUriGrant(any(), eq(ugm), eq(UID), eq(PACKAGE))
+
+ val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
+ val tile = content.getChildAt(1) as QSTileView
+
+ val iconDrawable = (tile.icon.iconView as ImageView).drawable.apply {
+ setTint(tintColor)
+ }
+
+ val defaultIcon = context.getDrawable(DEFAULT_ICON)!!.apply {
+ setTint(tintColor)
+ }
+
+ assertThat(areDrawablesEqual(iconDrawable, defaultIcon, size)).isTrue()
+ }
}
+
+private fun areDrawablesEqual(drawable1: Drawable, drawable2: Drawable, size: Int = 24): Boolean {
+ val bm1 = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
+ val bm2 = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
+
+ val canvas1 = Canvas(bm1)
+ val canvas2 = Canvas(bm2)
+
+ drawable1.setBounds(0, 0, size, size)
+ drawable2.setBounds(0, 0, size, size)
+
+ drawable1.draw(canvas1)
+ drawable2.draw(canvas2)
+
+ return equalBitmaps(bm1, bm2).also {
+ bm1.recycle()
+ bm2.recycle()
+ }
+}
+
+private fun equalBitmaps(a: Bitmap, b: Bitmap): Boolean {
+ if (a.width != b.width || a.height != b.height) return false
+ val w = a.width
+ val h = a.height
+ val aPix = IntArray(w * h)
+ val bPix = IntArray(w * h)
+ a.getPixels(aPix, 0, w, 0, 0, w, h)
+ b.getPixels(bPix, 0, w, 0, 0, w, h)
+ return Arrays.equals(aPix, bPix)
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
index ccfb5cf..3afa6ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.external
+import android.app.IUriGrantsManager
import android.app.StatusBarManager
import android.content.ComponentName
import android.content.DialogInterface
@@ -57,6 +58,7 @@
private val TEST_COMPONENT = ComponentName("test_pkg", "test_cls")
private const val TEST_APP_NAME = "App"
private const val TEST_LABEL = "Label"
+ private const val TEST_UID = 12345
}
@Mock
@@ -71,6 +73,8 @@
private lateinit var logger: TileRequestDialogEventLogger
@Mock
private lateinit var icon: Icon
+ @Mock
+ private lateinit var ugm: IUriGrantsManager
private val instanceIdSequence = InstanceIdSequenceFake(1_000)
private lateinit var controller: TileServiceRequestController
@@ -88,7 +92,8 @@
qsHost,
commandQueue,
commandRegistry,
- logger
+ logger,
+ ugm,
) {
tileRequestDialog
}
@@ -98,10 +103,24 @@
@Test
fun requestTileAdd_dataIsPassedToDialog() {
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, Callback())
+ controller.requestTileAdd(
+ TEST_UID,
+ TEST_COMPONENT,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ Callback(),
+ )
verify(tileRequestDialog).setTileData(
- TileRequestDialog.TileData(TEST_APP_NAME, TEST_LABEL, icon)
+ TileRequestDialog.TileData(
+ TEST_UID,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ TEST_COMPONENT.packageName,
+ ),
+ ugm,
)
}
@@ -110,7 +129,14 @@
`when`(qsHost.indexOf(CustomTile.toSpec(TEST_COMPONENT))).thenReturn(2)
val callback = Callback()
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
+ controller.requestTileAdd(
+ TEST_UID,
+ TEST_COMPONENT,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ callback,
+ )
assertThat(callback.lastAccepted).isEqualTo(TileServiceRequestController.TILE_ALREADY_ADDED)
verify(qsHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
@@ -120,7 +146,7 @@
fun tileAlreadyAdded_logged() {
`when`(qsHost.indexOf(CustomTile.toSpec(TEST_COMPONENT))).thenReturn(2)
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
+ controller.requestTileAdd(TEST_UID, TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
verify(logger).logTileAlreadyAdded(eq<String>(TEST_COMPONENT.packageName), any())
verify(logger, never()).logDialogShown(anyString(), any())
@@ -129,19 +155,33 @@
@Test
fun showAllUsers_set() {
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, Callback())
+ controller.requestTileAdd(
+ TEST_UID,
+ TEST_COMPONENT,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ Callback(),
+ )
verify(tileRequestDialog).setShowForAllUsers(true)
}
@Test
fun cancelOnTouchOutside_set() {
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, Callback())
+ controller.requestTileAdd(
+ TEST_UID,
+ TEST_COMPONENT,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ Callback(),
+ )
verify(tileRequestDialog).setCanceledOnTouchOutside(true)
}
@Test
fun dialogShown_logged() {
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
+ controller.requestTileAdd(TEST_UID, TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
verify(logger).logDialogShown(eq<String>(TEST_COMPONENT.packageName), any())
}
@@ -152,7 +192,14 @@
ArgumentCaptor.forClass(DialogInterface.OnCancelListener::class.java)
val callback = Callback()
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
+ controller.requestTileAdd(
+ TEST_UID,
+ TEST_COMPONENT,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ callback,
+ )
verify(tileRequestDialog).setOnCancelListener(capture(cancelListenerCaptor))
cancelListenerCaptor.value.onCancel(tileRequestDialog)
@@ -165,7 +212,7 @@
val cancelListenerCaptor =
ArgumentCaptor.forClass(DialogInterface.OnCancelListener::class.java)
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
+ controller.requestTileAdd(TEST_UID, TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
val instanceId = InstanceId.fakeInstanceId(instanceIdSequence.lastInstanceId)
verify(tileRequestDialog).setOnCancelListener(capture(cancelListenerCaptor))
@@ -185,7 +232,14 @@
ArgumentCaptor.forClass(DialogInterface.OnClickListener::class.java)
val callback = Callback()
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
+ controller.requestTileAdd(
+ TEST_UID,
+ TEST_COMPONENT,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ callback,
+ )
verify(tileRequestDialog).setPositiveButton(anyInt(), capture(clickListenerCaptor))
clickListenerCaptor.value.onClick(tileRequestDialog, DialogInterface.BUTTON_POSITIVE)
@@ -199,7 +253,7 @@
val clickListenerCaptor =
ArgumentCaptor.forClass(DialogInterface.OnClickListener::class.java)
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
+ controller.requestTileAdd(TEST_UID, TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
val instanceId = InstanceId.fakeInstanceId(instanceIdSequence.lastInstanceId)
verify(tileRequestDialog).setPositiveButton(anyInt(), capture(clickListenerCaptor))
@@ -219,7 +273,14 @@
ArgumentCaptor.forClass(DialogInterface.OnClickListener::class.java)
val callback = Callback()
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
+ controller.requestTileAdd(
+ TEST_UID,
+ TEST_COMPONENT,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ callback,
+ )
verify(tileRequestDialog).setNegativeButton(anyInt(), capture(clickListenerCaptor))
clickListenerCaptor.value.onClick(tileRequestDialog, DialogInterface.BUTTON_NEGATIVE)
@@ -233,7 +294,7 @@
val clickListenerCaptor =
ArgumentCaptor.forClass(DialogInterface.OnClickListener::class.java)
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
+ controller.requestTileAdd(TEST_UID, TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
val instanceId = InstanceId.fakeInstanceId(instanceIdSequence.lastInstanceId)
verify(tileRequestDialog).setNegativeButton(anyInt(), capture(clickListenerCaptor))
@@ -257,10 +318,24 @@
val captor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
verify(commandQueue, atLeastOnce()).addCallback(capture(captor))
- captor.value.requestAddTile(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, Callback())
+ captor.value.requestAddTile(
+ TEST_UID,
+ TEST_COMPONENT,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ Callback(),
+ )
verify(tileRequestDialog).setTileData(
- TileRequestDialog.TileData(TEST_APP_NAME, TEST_LABEL, icon)
+ TileRequestDialog.TileData(
+ TEST_UID,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ TEST_COMPONENT.packageName,
+ ),
+ ugm,
)
}
@@ -271,7 +346,7 @@
verify(commandQueue, atLeastOnce()).addCallback(capture(captor))
val c = Callback()
- captor.value.requestAddTile(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, c)
+ captor.value.requestAddTile(TEST_UID, TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, c)
assertThat(c.lastAccepted).isEqualTo(TileServiceRequestController.TILE_ALREADY_ADDED)
}
@@ -288,7 +363,14 @@
throw RemoteException()
}
}
- captor.value.requestAddTile(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
+ captor.value.requestAddTile(
+ TEST_UID,
+ TEST_COMPONENT,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ callback,
+ )
verify(tileRequestDialog).setOnCancelListener(capture(cancelListenerCaptor))
cancelListenerCaptor.value.onCancel(tileRequestDialog)
@@ -300,7 +382,14 @@
ArgumentCaptor.forClass(DialogInterface.OnDismissListener::class.java)
val callback = Callback()
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
+ controller.requestTileAdd(
+ TEST_UID,
+ TEST_COMPONENT,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ callback,
+ )
verify(tileRequestDialog).setOnDismissListener(capture(dismissListenerCaptor))
dismissListenerCaptor.value.onDismiss(tileRequestDialog)
@@ -317,7 +406,14 @@
ArgumentCaptor.forClass(DialogInterface.OnClickListener::class.java)
val callback = Callback()
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
+ controller.requestTileAdd(
+ TEST_UID,
+ TEST_COMPONENT,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ callback,
+ )
verify(tileRequestDialog).setPositiveButton(anyInt(), capture(clickListenerCaptor))
verify(tileRequestDialog).setOnDismissListener(capture(dismissListenerCaptor))
@@ -338,7 +434,14 @@
ArgumentCaptor.forClass(DialogInterface.OnCancelListener::class.java)
val callback = Callback()
- controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
+ controller.requestTileAdd(
+ TEST_UID,
+ TEST_COMPONENT,
+ TEST_APP_NAME,
+ TEST_LABEL,
+ icon,
+ callback,
+ )
verify(tileRequestDialog).setOnCancelListener(capture(cancelListenerCaptor))
verify(tileRequestDialog).setOnDismissListener(capture(dismissListenerCaptor))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index dc1b9c4..a750524 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -46,6 +46,7 @@
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.qs.tiles.di.NewQSTileFactory
import com.android.systemui.qs.toProto
import com.android.systemui.settings.UserTracker
import com.android.systemui.user.data.repository.FakeUserRepository
@@ -91,6 +92,8 @@
@Mock private lateinit var logger: QSPipelineLogger
+ @Mock private lateinit var newQSTileFactory: NewQSTileFactory
+
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -105,6 +108,8 @@
featureFlags.set(Flags.QS_PIPELINE_NEW_HOST, true)
featureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, true)
+ // TODO(b/299909337): Add test checking the new factory is used when the flag is on
+ featureFlags.set(Flags.QS_PIPELINE_NEW_TILES, true)
userRepository.setUserInfos(listOf(USER_INFO_0, USER_INFO_1))
@@ -117,6 +122,7 @@
userRepository = userRepository,
customTileStatePersister = customTileStatePersister,
tileFactory = tileFactory,
+ newQSTileFactory = { newQSTileFactory },
customTileAddedRepository = customTileAddedRepository,
tileLifecycleManagerFactory = tileLifecycleManagerFactory,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
index 5630b9d..2e6b50b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
@@ -1,6 +1,6 @@
package com.android.systemui.qs.pipeline.domain.interactor
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
@@ -21,7 +21,7 @@
import org.mockito.MockitoAnnotations
@RoboPilotTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class RestoreReconciliationInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
index e222542..067218a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
@@ -51,16 +51,17 @@
import com.android.systemui.qs.tiles.UiModeNightTile
import com.android.systemui.qs.tiles.WorkModeTile
import com.android.systemui.util.leak.GarbageMonitor
+import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
-import javax.inject.Provider
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Answers
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.inOrder
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import javax.inject.Provider
+import org.mockito.Mockito.`when` as whenever
private val specMap = mapOf(
"internet" to InternetTile::class.java,
@@ -98,7 +99,7 @@
class QSFactoryImplTest : SysuiTestCase() {
@Mock private lateinit var qsHost: QSHost
- @Mock(answer = Answers.RETURNS_SELF) private lateinit var customTileBuilder: CustomTile.Builder
+ @Mock private lateinit var customTileFactory: CustomTile.Factory
@Mock private lateinit var customTile: CustomTile
@Mock private lateinit var internetTile: InternetTile
@@ -139,7 +140,7 @@
whenever(qsHost.context).thenReturn(mContext)
whenever(qsHost.userContext).thenReturn(mContext)
- whenever(customTileBuilder.build()).thenReturn(customTile)
+ whenever(customTileFactory.create(anyString(), any())).thenReturn(customTile)
val tileMap = mutableMapOf<String, Provider<QSTileImpl<*>>>(
"internet" to Provider { internetTile },
@@ -174,7 +175,7 @@
factory = QSFactoryImpl(
{ qsHost },
- { customTileBuilder },
+ { customTileFactory },
tileMap,
)
// When adding/removing tiles, fix also [specMap] and [tileMap]
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
deleted file mode 100644
index 47b4244..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.android.systemui.qs.tiles.base
-
-import android.content.Intent
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserActionHandler
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.any
-import org.mockito.Mockito.eq
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class QSTileIntentUserActionHandlerTest : SysuiTestCase() {
-
- @Mock private lateinit var activityStarted: ActivityStarter
-
- lateinit var underTest: QSTileIntentUserActionHandler
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- underTest = QSTileIntentUserActionHandler(activityStarted)
- }
-
- @Test
- fun testPassesIntentToStarter() {
- val intent = Intent("test.ACTION")
-
- underTest.handle(QSTileUserAction.Click(context, null), intent)
-
- verify(activityStarted).postStartActivityDismissingKeyguard(eq(intent), eq(0), any())
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt
new file mode 100644
index 0000000..06b7a9f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.qs.tiles.base.actions
+
+import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
+class QSTileIntentUserActionHandlerTest : SysuiTestCase() {
+
+ @Mock private lateinit var activityStarted: ActivityStarter
+
+ lateinit var underTest: QSTileIntentUserActionHandler
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest = QSTileIntentUserActionHandler(activityStarted)
+ }
+
+ @Test
+ fun testPassesIntentToStarter() {
+ val intent = Intent("test.ACTION")
+
+ underTest.handle(null, intent)
+
+ verify(activityStarted).postStartActivityDismissingKeyguard(eq(intent), eq(0), any())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
new file mode 100644
index 0000000..4f25d12
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
@@ -0,0 +1,141 @@
+/*
+ * 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.qs.tiles.base.interactor
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.RestrictedLockUtils
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
+class DisabledByPolicyInteractorTest : SysuiTestCase() {
+
+ @Mock private lateinit var restrictedLockProxy: RestrictedLockProxy
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var context: Context
+
+ @Captor private lateinit var intentCaptor: ArgumentCaptor<Intent>
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ lateinit var underTest: DisabledByPolicyInteractor
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ DisabledByPolicyInteractorImpl(
+ context,
+ activityStarter,
+ restrictedLockProxy,
+ testDispatcher,
+ )
+ }
+
+ @Test
+ fun testEnabledWhenNoAdmin() =
+ testScope.runTest {
+ whenever(restrictedLockProxy.getEnforcedAdmin(anyInt(), anyString())).thenReturn(null)
+
+ assertThat(underTest.isDisabled(TEST_USER, TEST_RESTRICTION))
+ .isSameInstanceAs(DisabledByPolicyInteractor.PolicyResult.TileEnabled)
+ }
+
+ @Test
+ fun testDisabledWhenAdminWithNoRestrictions() =
+ testScope.runTest {
+ val admin = EnforcedAdmin(TEST_COMPONENT_NAME, UserHandle(TEST_USER))
+ whenever(restrictedLockProxy.getEnforcedAdmin(anyInt(), anyString())).thenReturn(admin)
+ whenever(restrictedLockProxy.hasBaseUserRestriction(anyInt(), anyString()))
+ .thenReturn(false)
+
+ val result =
+ underTest.isDisabled(TEST_USER, TEST_RESTRICTION)
+ as DisabledByPolicyInteractor.PolicyResult.TileDisabled
+ assertThat(result.admin).isEqualTo(admin)
+ }
+
+ @Test
+ fun testEnabledWhenAdminWithRestrictions() =
+ testScope.runTest {
+ whenever(restrictedLockProxy.getEnforcedAdmin(anyInt(), anyString())).thenReturn(ADMIN)
+ whenever(restrictedLockProxy.hasBaseUserRestriction(anyInt(), anyString()))
+ .thenReturn(true)
+
+ assertThat(underTest.isDisabled(TEST_USER, TEST_RESTRICTION))
+ .isSameInstanceAs(DisabledByPolicyInteractor.PolicyResult.TileEnabled)
+ }
+
+ @Test
+ fun testHandleDisabledByPolicy() {
+ val result =
+ underTest.handlePolicyResult(
+ DisabledByPolicyInteractor.PolicyResult.TileDisabled(ADMIN)
+ )
+
+ val expectedIntent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(context, ADMIN)
+ assertThat(result).isTrue()
+ verify(activityStarter).postStartActivityDismissingKeyguard(intentCaptor.capture(), any())
+ assertThat(intentCaptor.value.filterEquals(expectedIntent)).isTrue()
+ }
+
+ @Test
+ fun testHandleEnabled() {
+ val result =
+ underTest.handlePolicyResult(DisabledByPolicyInteractor.PolicyResult.TileEnabled)
+
+ assertThat(result).isFalse()
+ verify(activityStarter, never())
+ .postStartActivityDismissingKeyguard(intentCaptor.capture(), any())
+ }
+
+ private companion object {
+ const val TEST_USER = 1
+ const val TEST_RESTRICTION = "test_restriction"
+
+ val TEST_COMPONENT_NAME = ComponentName("test.pkg", "test.cls")
+
+ val ADMIN = EnforcedAdmin(TEST_COMPONENT_NAME, UserHandle(TEST_USER))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
index 643866e..9024c6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
@@ -1,12 +1,16 @@
package com.android.systemui.qs.tiles.viewmodel
-import android.graphics.drawable.Icon
-import android.testing.AndroidTestingRunner
+import android.graphics.drawable.ShapeDrawable
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
+import com.android.internal.logging.InstanceId
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
import com.android.systemui.qs.tiles.base.interactor.QSTileDataRequest
@@ -26,12 +30,13 @@
// TODO(b/299909368): Add more tests
@MediumTest
@RoboPilotTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() {
private val fakeQSTileDataInteractor = FakeQSTileDataInteractor<Any>()
private val fakeQSTileUserActionInteractor = FakeQSTileUserActionInteractor<Any>()
+ private val fakeDisabledByPolicyInteractor = FakeDisabledByPolicyInteractor()
private val testCoroutineDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testCoroutineDispatcher)
@@ -65,26 +70,27 @@
scope: TestScope,
config: QSTileConfig = TEST_QS_TILE_CONFIG,
): QSTileViewModel =
- object :
- BaseQSTileViewModel<Any>(
- config,
- fakeQSTileUserActionInteractor,
- fakeQSTileDataInteractor,
- object : QSTileDataToStateMapper<Any> {
- override fun map(config: QSTileConfig, data: Any): QSTileState {
- return QSTileState(config.tileIcon, config.tileLabel)
- }
- },
- testCoroutineDispatcher,
- tileScope = scope.backgroundScope,
- ) {}
+ BaseQSTileViewModel(
+ config,
+ fakeQSTileUserActionInteractor,
+ fakeQSTileDataInteractor,
+ object : QSTileDataToStateMapper<Any> {
+ override fun map(config: QSTileConfig, data: Any): QSTileState =
+ QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {}
+ },
+ fakeDisabledByPolicyInteractor,
+ testCoroutineDispatcher,
+ scope.backgroundScope,
+ )
private companion object {
+
val TEST_QS_TILE_CONFIG =
QSTileConfig(
TileSpec.create("default"),
- Icon.createWithContentUri(""),
- "",
+ Icon.Loaded(ShapeDrawable(), null),
+ 0,
+ InstanceId.fakeInstanceId(0),
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 8ae8930..f1c99d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -48,11 +48,6 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
- private val authenticationInteractor =
- utils.authenticationInteractor(
- repository = utils.authenticationRepository(),
- )
-
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
private var mobileIconsViewModel: MobileIconsViewModel =
@@ -85,10 +80,17 @@
broadcastDispatcher = fakeBroadcastDispatcher,
)
+ val authenticationInteractor = utils.authenticationInteractor()
+
underTest =
QuickSettingsSceneViewModel(
bouncerInteractor =
utils.bouncerInteractor(
+ deviceEntryInteractor =
+ utils.deviceEntryInteractor(
+ authenticationInteractor = authenticationInteractor,
+ sceneInteractor = sceneInteractor,
+ ),
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
),
@@ -101,7 +103,7 @@
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(true)
+ utils.deviceEntryRepository.setUnlocked(true)
runCurrent()
underTest.onContentClicked()
@@ -114,7 +116,7 @@
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
runCurrent()
underTest.onContentClicked()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 85bd92b..5259013 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -83,7 +83,6 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
-
private val sceneContainerConfig = utils.fakeSceneContainerConfig()
private val sceneRepository =
utils.fakeSceneContainerRepository(
@@ -93,14 +92,12 @@
utils.sceneInteractor(
repository = sceneRepository,
)
-
- private val authenticationRepository = utils.authenticationRepository()
- private val authenticationInteractor =
- utils.authenticationInteractor(
- repository = authenticationRepository,
+ private val authenticationInteractor = utils.authenticationInteractor()
+ private val deviceEntryInteractor =
+ utils.deviceEntryInteractor(
+ authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
)
-
private val communalInteractor = utils.communalInteractor()
private val transitionState =
@@ -116,6 +113,7 @@
private val bouncerInteractor =
utils.bouncerInteractor(
+ deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
)
@@ -128,7 +126,7 @@
private val lockscreenSceneViewModel =
LockscreenSceneViewModel(
applicationScope = testScope.backgroundScope,
- authenticationInteractor = authenticationInteractor,
+ deviceEntryInteractor = deviceEntryInteractor,
communalInteractor = communalInteractor,
longPress =
KeyguardLongPressViewModel(
@@ -178,12 +176,12 @@
shadeSceneViewModel =
ShadeSceneViewModel(
applicationScope = testScope.backgroundScope,
- authenticationInteractor = authenticationInteractor,
+ deviceEntryInteractor = deviceEntryInteractor,
bouncerInteractor = bouncerInteractor,
shadeHeaderViewModel = shadeHeaderViewModel,
)
- authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
val displayTracker = FakeDisplayTracker(context)
val sysUiState = SysUiState(displayTracker)
@@ -191,6 +189,7 @@
SceneContainerStartable(
applicationScope = testScope.backgroundScope,
sceneInteractor = sceneInteractor,
+ deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
keyguardInteractor = keyguardInteractor,
flags = utils.sceneContainerFlags,
@@ -417,13 +416,13 @@
// Set the lockscreen enabled bit _before_ set the auth method as the code picks up on the
// lockscreen enabled bit _after_ the auth method is changed and the lockscreen enabled bit
// is not an observable that can trigger a new evaluation.
- authenticationRepository.setLockscreenEnabled(
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(
authMethod !is DomainLayerAuthenticationMethodModel.None
)
- authenticationRepository.setAuthenticationMethod(authMethod.toDataLayer())
+ utils.authenticationRepository.setAuthenticationMethod(authMethod.toDataLayer())
if (!authMethod.isSecure) {
// When the auth method is not secure, the device is never considered locked.
- authenticationRepository.setUnlocked(true)
+ utils.deviceEntryRepository.setUnlocked(true)
}
runCurrent()
}
@@ -528,14 +527,14 @@
.that(authMethod.isSecure)
.isTrue()
- authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
runCurrent()
}
/** Unlocks the device by entering the correct PIN. Ends up in the Gone scene. */
private fun TestScope.unlockDevice() {
assertWithMessage("Cannot unlock a device that's already unlocked!")
- .that(authenticationInteractor.isUnlocked.value)
+ .that(deviceEntryInteractor.isUnlocked.value)
.isFalse()
emulateUserDrivenTransition(SceneKey.Bouncer)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 16fdf8e..00a20cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -59,17 +59,13 @@
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
private val sceneContainerFlags = utils.sceneContainerFlags
- private val authenticationRepository = utils.authenticationRepository()
- private val authenticationInteractor =
- utils.authenticationInteractor(
- repository = authenticationRepository,
+ private val authenticationInteractor = utils.authenticationInteractor()
+ private val deviceEntryInteractor =
+ utils.deviceEntryInteractor(
+ authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
)
- private val keyguardRepository = utils.keyguardRepository
- private val keyguardInteractor =
- utils.keyguardInteractor(
- repository = keyguardRepository,
- )
+ private val keyguardInteractor = utils.keyguardInteractor()
private val sysUiState: SysUiState = mock()
private val falsingCollector: FalsingCollector = mock()
@@ -77,6 +73,7 @@
SceneContainerStartable(
applicationScope = testScope.backgroundScope,
sceneInteractor = sceneInteractor,
+ deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
keyguardInteractor = keyguardInteractor,
flags = sceneContainerFlags,
@@ -141,7 +138,7 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
underTest.start()
- authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@@ -157,7 +154,7 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
underTest.start()
- authenticationRepository.setUnlocked(true)
+ utils.deviceEntryRepository.setUnlocked(true)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
@@ -173,7 +170,7 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
- authenticationRepository.setUnlocked(true)
+ utils.deviceEntryRepository.setUnlocked(true)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
@@ -189,7 +186,7 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
- authenticationRepository.setUnlocked(true)
+ utils.deviceEntryRepository.setUnlocked(true)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@@ -205,7 +202,7 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Shade)
underTest.start()
- keyguardRepository.setWakefulnessModel(STARTING_TO_SLEEP)
+ utils.keyguardRepository.setWakefulnessModel(STARTING_TO_SLEEP)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@@ -251,7 +248,7 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
@@ -267,7 +264,7 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@@ -283,7 +280,7 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@@ -300,9 +297,9 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
- authenticationRepository.setUnlocked(true)
+ utils.deviceEntryRepository.setUnlocked(true)
runCurrent()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
@@ -389,11 +386,11 @@
runCurrent()
verify(falsingCollector).setShowingAod(false)
- keyguardRepository.setIsDozing(true)
+ utils.keyguardRepository.setIsDozing(true)
runCurrent()
verify(falsingCollector).setShowingAod(true)
- keyguardRepository.setIsDozing(false)
+ utils.keyguardRepository.setIsDozing(false)
runCurrent()
verify(falsingCollector, times(2)).setShowingAod(false)
}
@@ -401,7 +398,7 @@
@Test
fun collectFalsingSignals_screenOnAndOff_aodUnavailable() =
testScope.runTest {
- keyguardRepository.setAodAvailable(false)
+ utils.keyguardRepository.setAodAvailable(false)
runCurrent()
prepareState(
initialSceneKey = SceneKey.Lockscreen,
@@ -414,31 +411,31 @@
verify(falsingCollector, never()).onScreenOnFromTouch()
verify(falsingCollector, never()).onScreenOff()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
runCurrent()
verify(falsingCollector, times(1)).onScreenTurningOn()
verify(falsingCollector, never()).onScreenOnFromTouch()
verify(falsingCollector, never()).onScreenOff()
- keyguardRepository.setWakefulnessModel(ASLEEP)
+ utils.keyguardRepository.setWakefulnessModel(ASLEEP)
runCurrent()
verify(falsingCollector, times(1)).onScreenTurningOn()
verify(falsingCollector, never()).onScreenOnFromTouch()
verify(falsingCollector, times(1)).onScreenOff()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_TAP)
+ utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_TAP)
runCurrent()
verify(falsingCollector, times(1)).onScreenTurningOn()
verify(falsingCollector, times(1)).onScreenOnFromTouch()
verify(falsingCollector, times(1)).onScreenOff()
- keyguardRepository.setWakefulnessModel(ASLEEP)
+ utils.keyguardRepository.setWakefulnessModel(ASLEEP)
runCurrent()
verify(falsingCollector, times(1)).onScreenTurningOn()
verify(falsingCollector, times(1)).onScreenOnFromTouch()
verify(falsingCollector, times(2)).onScreenOff()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
runCurrent()
verify(falsingCollector, times(2)).onScreenTurningOn()
verify(falsingCollector, times(1)).onScreenOnFromTouch()
@@ -448,7 +445,7 @@
@Test
fun collectFalsingSignals_screenOnAndOff_aodAvailable() =
testScope.runTest {
- keyguardRepository.setAodAvailable(true)
+ utils.keyguardRepository.setAodAvailable(true)
runCurrent()
prepareState(
initialSceneKey = SceneKey.Lockscreen,
@@ -461,31 +458,31 @@
verify(falsingCollector, never()).onScreenOnFromTouch()
verify(falsingCollector, never()).onScreenOff()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
runCurrent()
verify(falsingCollector, never()).onScreenTurningOn()
verify(falsingCollector, never()).onScreenOnFromTouch()
verify(falsingCollector, never()).onScreenOff()
- keyguardRepository.setWakefulnessModel(ASLEEP)
+ utils.keyguardRepository.setWakefulnessModel(ASLEEP)
runCurrent()
verify(falsingCollector, never()).onScreenTurningOn()
verify(falsingCollector, never()).onScreenOnFromTouch()
verify(falsingCollector, never()).onScreenOff()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_TAP)
+ utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_TAP)
runCurrent()
verify(falsingCollector, never()).onScreenTurningOn()
verify(falsingCollector, never()).onScreenOnFromTouch()
verify(falsingCollector, never()).onScreenOff()
- keyguardRepository.setWakefulnessModel(ASLEEP)
+ utils.keyguardRepository.setWakefulnessModel(ASLEEP)
runCurrent()
verify(falsingCollector, never()).onScreenTurningOn()
verify(falsingCollector, never()).onScreenOnFromTouch()
verify(falsingCollector, never()).onScreenOff()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
runCurrent()
verify(falsingCollector, never()).onScreenTurningOn()
verify(falsingCollector, never()).onScreenOnFromTouch()
@@ -521,8 +518,8 @@
): MutableStateFlow<ObservableTransitionState> {
assumeTrue(Flags.SCENE_CONTAINER_ENABLED)
sceneContainerFlags.enabled = true
- authenticationRepository.setUnlocked(isDeviceUnlocked)
- keyguardRepository.setBypassEnabled(isBypassEnabled)
+ utils.deviceEntryRepository.setUnlocked(isDeviceUnlocked)
+ utils.deviceEntryRepository.setBypassEnabled(isBypassEnabled)
val transitionStateFlow =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Idle(SceneKey.Lockscreen)
@@ -534,8 +531,10 @@
sceneInteractor.onSceneChanged(SceneModel(it), "reason")
}
authenticationMethod?.let {
- authenticationRepository.setAuthenticationMethod(authenticationMethod.toDataLayer())
- authenticationRepository.setLockscreenEnabled(
+ utils.authenticationRepository.setAuthenticationMethod(
+ authenticationMethod.toDataLayer()
+ )
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(
authenticationMethod != AuthenticationMethodModel.None
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 59b5953..a2aed98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -41,7 +41,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.media.MediaProjectionCaptureTarget;
+import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index d470d24..3ae1f35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -22,11 +22,13 @@
import android.view.View
import android.widget.Spinner
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
+import com.android.systemui.mediaprojection.permission.SINGLE_APP
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -35,8 +37,8 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 2d00e8c..2ed2090 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -28,6 +28,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -46,22 +47,34 @@
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.scene.FakeWindowRootViewComponent;
+import com.android.systemui.scene.SceneTestUtils;
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
+import com.android.systemui.shade.data.repository.FakeShadeRepository;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
+import com.android.systemui.user.domain.interactor.UserInteractor;
import com.google.common.util.concurrent.MoreExecutors;
@@ -77,8 +90,10 @@
import java.util.List;
import java.util.concurrent.Executor;
+import kotlinx.coroutines.test.TestScope;
+
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
@SmallTest
public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@@ -102,6 +117,9 @@
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
private final Executor mBackgroundExecutor = MoreExecutors.directExecutor();
+ private SceneTestUtils mUtils = new SceneTestUtils(this);
+ private TestScope mTestScope = mUtils.getTestScope();
+ private ShadeInteractor mShadeInteractor;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private float mPreferredRefreshRate = -1;
@@ -119,6 +137,23 @@
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
+ mShadeInteractor =
+ new ShadeInteractor(
+ mTestScope.getBackgroundScope(),
+ new FakeDisableFlagsRepository(),
+ new FakeSceneContainerFlags(),
+ mUtils::sceneInteractor,
+ new FakeKeyguardRepository(),
+ new FakeUserSetupRepository(),
+ mock(DeviceProvisionedController.class),
+ mock(UserInteractor.class),
+ new SharedNotificationContainerInteractor(
+ new FakeConfigurationRepository(),
+ mContext,
+ new ResourcesSplitShadeStateController()),
+ new FakeShadeRepository()
+ );
+
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
mContext,
new FakeWindowRootViewComponent.Factory(mNotificationShadeWindowView),
@@ -136,6 +171,7 @@
mScreenOffAnimationController,
mAuthController,
mShadeExpansionStateManager,
+ () -> mShadeInteractor,
mShadeWindowLogger) {
@Override
protected boolean isDebuggable() {
@@ -272,9 +308,9 @@
@Test
public void setPanelExpanded_notFocusable_altFocusable_whenPanelIsOpen() {
- mNotificationShadeWindowController.onShadeExpansionFullyChanged(true);
+ mNotificationShadeWindowController.onShadeOrQsExpanded(true);
clearInvocations(mWindowManager);
- mNotificationShadeWindowController.onShadeExpansionFullyChanged(true);
+ mNotificationShadeWindowController.onShadeOrQsExpanded(true);
verifyNoMoreInteractions(mWindowManager);
mNotificationShadeWindowController.setNotificationShadeFocusable(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 5c75d9c..602bd5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -47,9 +47,10 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
- private val authenticationInteractor =
- utils.authenticationInteractor(
- repository = utils.authenticationRepository(),
+ private val authenticationInteractor = utils.authenticationInteractor()
+ private val deviceEntryInteractor =
+ utils.deviceEntryInteractor(
+ authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
)
@@ -88,9 +89,10 @@
underTest =
ShadeSceneViewModel(
applicationScope = testScope.backgroundScope,
- authenticationInteractor = authenticationInteractor,
+ deviceEntryInteractor = deviceEntryInteractor,
bouncerInteractor =
utils.bouncerInteractor(
+ deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
),
@@ -103,7 +105,7 @@
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@@ -113,7 +115,7 @@
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(true)
+ utils.deviceEntryRepository.setUnlocked(true)
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone)
}
@@ -122,7 +124,7 @@
fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
- utils.authenticationRepository.setLockscreenEnabled(true)
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
@@ -134,7 +136,7 @@
fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
- utils.authenticationRepository.setLockscreenEnabled(true)
+ utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
@@ -147,7 +149,7 @@
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(true)
+ utils.deviceEntryRepository.setUnlocked(true)
runCurrent()
underTest.onContentClicked()
@@ -160,7 +162,7 @@
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
+ utils.deviceEntryRepository.setUnlocked(false)
runCurrent()
underTest.onContentClicked()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 48665fe..e714736 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.shared.clocks
+import android.content.ComponentName
import android.content.ContentResolver
import android.content.Context
import android.graphics.drawable.Drawable
@@ -28,12 +29,11 @@
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProviderPlugin
import com.android.systemui.plugins.ClockSettings
-import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginLifecycleManager
+import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
import kotlinx.coroutines.CoroutineDispatcher
@@ -46,6 +46,7 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -66,7 +67,6 @@
@Mock private lateinit var mockDefaultClock: ClockController
@Mock private lateinit var mockThumbnail: Drawable
@Mock private lateinit var mockContentResolver: ContentResolver
- @Mock private lateinit var mockPluginLifecycle: PluginLifecycleManager<ClockProviderPlugin>
private lateinit var fakeDefaultProvider: FakeClockPlugin
private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
private lateinit var registry: ClockRegistry
@@ -84,6 +84,41 @@
}
}
+ private class FakeLifecycle(
+ private val tag: String,
+ private val plugin: ClockProviderPlugin?,
+ ) : PluginLifecycleManager<ClockProviderPlugin> {
+ var onLoad: (() -> Unit)? = null
+ var onUnload: (() -> Unit)? = null
+
+ private var mIsLoaded: Boolean = true
+ override fun isLoaded() = mIsLoaded
+ override fun getPlugin(): ClockProviderPlugin? = if (isLoaded) plugin else null
+
+ var mComponentName = ComponentName("Package[$tag]", "Class[$tag]")
+ override fun toString() = "Manager[$tag]"
+ override fun getPackage(): String = mComponentName.getPackageName()
+ override fun getComponentName(): ComponentName = mComponentName
+
+ private var isDebug: Boolean = false
+ override fun getIsDebug(): Boolean = isDebug
+ override fun setIsDebug(value: Boolean) { isDebug = value }
+
+ override fun loadPlugin() {
+ if (!mIsLoaded) {
+ mIsLoaded = true
+ onLoad?.invoke()
+ }
+ }
+
+ override fun unloadPlugin() {
+ if (mIsLoaded) {
+ mIsLoaded = false
+ onUnload?.invoke()
+ }
+ }
+ }
+
private class FakeClockPlugin : ClockProviderPlugin {
private val metadata = mutableListOf<ClockMetadata>()
private val createCallbacks = mutableMapOf<ClockId, (ClockId) -> ClockController>()
@@ -150,13 +185,15 @@
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
+ val lifecycle1 = FakeLifecycle("1", plugin1)
val plugin2 = FakeClockPlugin()
.addClock("clock_3", "clock 3")
.addClock("clock_4", "clock 4")
+ val lifecycle2 = FakeLifecycle("2", plugin2)
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
val list = registry.getClocks()
assertEquals(
list.toSet(),
@@ -178,18 +215,18 @@
@Test
fun clockIdConflict_ErrorWithoutCrash_unloadDuplicate() {
- val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1", { mockClock }, { mockThumbnail })
.addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail })
+ val lifecycle1 = spy(FakeLifecycle("1", plugin1))
- val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin2 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
+ val lifecycle2 = spy(FakeLifecycle("2", plugin2))
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
val list = registry.getClocks()
assertEquals(
list.toSet(),
@@ -204,8 +241,8 @@
assertEquals(registry.createExampleClock("clock_2"), mockClock)
assertEquals(registry.getClockThumbnail("clock_1"), mockThumbnail)
assertEquals(registry.getClockThumbnail("clock_2"), mockThumbnail)
- verify(mockPluginLifecycle1, never()).unloadPlugin()
- verify(mockPluginLifecycle2, times(2)).unloadPlugin()
+ verify(lifecycle1, never()).unloadPlugin()
+ verify(lifecycle2, times(2)).unloadPlugin()
}
@Test
@@ -213,14 +250,16 @@
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
+ val lifecycle1 = spy(FakeLifecycle("1", plugin1))
val plugin2 = FakeClockPlugin()
.addClock("clock_3", "clock 3", { mockClock })
.addClock("clock_4", "clock 4")
+ val lifecycle2 = spy(FakeLifecycle("2", plugin2))
registry.applySettings(ClockSettings("clock_3", null))
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
val clock = registry.createCurrentClock()
assertEquals(mockClock, clock)
@@ -231,17 +270,19 @@
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
+ val lifecycle1 = spy(FakeLifecycle("1", plugin1))
val plugin2 = FakeClockPlugin()
.addClock("clock_3", "clock 3", { mockClock })
.addClock("clock_4", "clock 4")
+ val lifecycle2 = spy(FakeLifecycle("2", plugin2))
registry.applySettings(ClockSettings("clock_3", null))
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
assertEquals(DEFAULT_CLOCK_ID, registry.activeClockId)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
assertEquals("clock_3", registry.activeClockId)
}
@@ -250,15 +291,17 @@
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
+ val lifecycle1 = spy(FakeLifecycle("1", plugin1))
val plugin2 = FakeClockPlugin()
.addClock("clock_3", "clock 3")
.addClock("clock_4", "clock 4")
+ val lifecycle2 = spy(FakeLifecycle("2", plugin2))
registry.applySettings(ClockSettings("clock_3", null))
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
- pluginListener.onPluginUnloaded(plugin2, mockPluginLifecycle)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
+ pluginListener.onPluginUnloaded(plugin2, lifecycle2)
val clock = registry.createCurrentClock()
assertEquals(clock, mockDefaultClock)
@@ -266,15 +309,15 @@
@Test
fun pluginRemoved_clockAndListChanged() {
- val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
+ val lifecycle1 = spy(FakeLifecycle("1", plugin1))
- val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin2 = FakeClockPlugin()
.addClock("clock_3", "clock 3", { mockClock })
.addClock("clock_4", "clock 4")
+ val lifecycle2 = spy(FakeLifecycle("2", plugin2))
var changeCallCount = 0
var listChangeCallCount = 0
@@ -288,32 +331,32 @@
assertEquals(1, changeCallCount)
assertEquals(0, listChangeCallCount)
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
scheduler.runCurrent()
assertEquals(1, changeCallCount)
assertEquals(1, listChangeCallCount)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
scheduler.runCurrent()
assertEquals(2, changeCallCount)
assertEquals(2, listChangeCallCount)
- pluginListener.onPluginUnloaded(plugin1, mockPluginLifecycle1)
+ pluginListener.onPluginUnloaded(plugin1, lifecycle1)
scheduler.runCurrent()
assertEquals(2, changeCallCount)
assertEquals(2, listChangeCallCount)
- pluginListener.onPluginUnloaded(plugin2, mockPluginLifecycle2)
+ pluginListener.onPluginUnloaded(plugin2, lifecycle2)
scheduler.runCurrent()
assertEquals(3, changeCallCount)
assertEquals(2, listChangeCallCount)
- pluginListener.onPluginDetached(mockPluginLifecycle1)
+ pluginListener.onPluginDetached(lifecycle1)
scheduler.runCurrent()
assertEquals(3, changeCallCount)
assertEquals(3, listChangeCallCount)
- pluginListener.onPluginDetached(mockPluginLifecycle2)
+ pluginListener.onPluginDetached(lifecycle2)
scheduler.runCurrent()
assertEquals(3, changeCallCount)
assertEquals(4, listChangeCallCount)
@@ -321,8 +364,9 @@
@Test
fun unknownPluginAttached_clockAndListUnchanged_loadRequested() {
- val mockPluginLifecycle = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- whenever(mockPluginLifecycle.getPackage()).thenReturn("some.other.package")
+ val lifecycle = FakeLifecycle("", null).apply {
+ mComponentName = ComponentName("some.other.package", "SomeClass")
+ }
var changeCallCount = 0
var listChangeCallCount = 0
@@ -331,7 +375,7 @@
override fun onAvailableClocksChanged() { listChangeCallCount++ }
})
- assertEquals(true, pluginListener.onPluginAttached(mockPluginLifecycle))
+ assertEquals(true, pluginListener.onPluginAttached(lifecycle))
scheduler.runCurrent()
assertEquals(0, changeCallCount)
assertEquals(0, listChangeCallCount)
@@ -339,10 +383,12 @@
@Test
fun knownPluginAttached_clockAndListChanged_notLoaded() {
- val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- whenever(mockPluginLifecycle1.getPackage()).thenReturn("com.android.systemui.clocks.metro")
- val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- whenever(mockPluginLifecycle2.getPackage()).thenReturn("com.android.systemui.clocks.bignum")
+ val lifecycle1 = FakeLifecycle("Metro", null).apply {
+ mComponentName = ComponentName("com.android.systemui.clocks.metro", "MetroClock")
+ }
+ val lifecycle2 = FakeLifecycle("BigNum", null).apply {
+ mComponentName = ComponentName("com.android.systemui.clocks.bignum", "BigNumClock")
+ }
var changeCallCount = 0
var listChangeCallCount = 0
@@ -356,12 +402,12 @@
assertEquals(1, changeCallCount)
assertEquals(0, listChangeCallCount)
- assertEquals(false, pluginListener.onPluginAttached(mockPluginLifecycle1))
+ assertEquals(false, pluginListener.onPluginAttached(lifecycle1))
scheduler.runCurrent()
assertEquals(1, changeCallCount)
assertEquals(1, listChangeCallCount)
- assertEquals(false, pluginListener.onPluginAttached(mockPluginLifecycle2))
+ assertEquals(false, pluginListener.onPluginAttached(lifecycle2))
scheduler.runCurrent()
assertEquals(1, changeCallCount)
assertEquals(2, listChangeCallCount)
@@ -369,18 +415,14 @@
@Test
fun pluginAddRemove_concurrentModification() {
- val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- val mockPluginLifecycle3 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- val mockPluginLifecycle4 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin1 = FakeClockPlugin().addClock("clock_1", "clock 1")
+ val lifecycle1 = FakeLifecycle("1", plugin1)
val plugin2 = FakeClockPlugin().addClock("clock_2", "clock 2")
+ val lifecycle2 = FakeLifecycle("2", plugin2)
val plugin3 = FakeClockPlugin().addClock("clock_3", "clock 3")
+ val lifecycle3 = FakeLifecycle("3", plugin3)
val plugin4 = FakeClockPlugin().addClock("clock_4", "clock 4")
- whenever(mockPluginLifecycle1.isLoaded).thenReturn(true)
- whenever(mockPluginLifecycle2.isLoaded).thenReturn(true)
- whenever(mockPluginLifecycle3.isLoaded).thenReturn(true)
- whenever(mockPluginLifecycle4.isLoaded).thenReturn(true)
+ val lifecycle4 = FakeLifecycle("4", plugin4)
// Set the current clock to the final clock to load
registry.applySettings(ClockSettings("clock_4", null))
@@ -390,15 +432,15 @@
// unload other plugins. This causes ClockRegistry to modify the list of available clock
// plugins while it is being iterated over. In production this happens as a result of a
// thread race, instead of synchronously like it does here.
- whenever(mockPluginLifecycle2.unloadPlugin()).then {
- pluginListener.onPluginDetached(mockPluginLifecycle1)
- pluginListener.onPluginLoaded(plugin4, mockContext, mockPluginLifecycle4)
+ lifecycle2.onUnload = {
+ pluginListener.onPluginDetached(lifecycle1)
+ pluginListener.onPluginLoaded(plugin4, mockContext, lifecycle4)
}
// Load initial plugins
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
- pluginListener.onPluginLoaded(plugin3, mockContext, mockPluginLifecycle3)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
+ pluginListener.onPluginLoaded(plugin3, mockContext, lifecycle3)
// Repeatedly verify the loaded providers to get final state
registry.verifyLoadedProviders()
@@ -484,11 +526,11 @@
private fun testTransitClockFlag(flag: Boolean) {
featureFlags.set(TRANSIT_CLOCK, flag)
registry.isTransitClockEnabled = featureFlags.isEnabled(TRANSIT_CLOCK)
- val mockPluginLifecycle = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("DIGITAL_CLOCK_METRO", "metro clock")
- pluginListener.onPluginLoaded(plugin, mockContext, mockPluginLifecycle)
+ val lifecycle = FakeLifecycle("metro", plugin)
+ pluginListener.onPluginLoaded(plugin, mockContext, lifecycle)
val list = registry.getClocks()
if (flag) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 39accfb..19863ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,9 +16,18 @@
package com.android.systemui.statusbar;
+import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+import static android.os.UserHandle.USER_ALL;
+import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -32,6 +41,7 @@
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.os.Handler;
@@ -49,8 +59,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.UserTracker;
@@ -66,6 +74,7 @@
import com.google.android.collect.Lists;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -112,7 +121,6 @@
private NotificationEntry mCurrentUserNotif;
private NotificationEntry mSecondaryUserNotif;
private NotificationEntry mWorkProfileNotif;
- private final FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags();
@Before
public void setUp() {
@@ -127,8 +135,11 @@
mWorkUser = new UserInfo(currentUserId + 2, "" /* name */, null /* iconPath */, 0,
UserManager.USER_TYPE_PROFILE_MANAGED);
+ when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(true);
when(mUserManager.getProfiles(currentUserId)).thenReturn(Lists.newArrayList(
- mCurrentUser, mSecondaryUser, mWorkUser));
+ mCurrentUser, mWorkUser));
+ when(mUserManager.getProfiles(mSecondaryUser.id)).thenReturn(Lists.newArrayList(
+ mSecondaryUser));
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
Handler.createAsync(Looper.myLooper()));
@@ -153,14 +164,14 @@
@Test
public void testLockScreenShowNotificationsFalse() {
- mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+ mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications());
}
@Test
public void testLockScreenShowNotificationsTrue() {
- mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications());
}
@@ -227,6 +238,7 @@
// THEN work profile notification is redacted
assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+ assertFalse(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
}
@Test
@@ -238,6 +250,7 @@
// THEN work profile notification isn't redacted
assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+ assertTrue(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
}
@Test
@@ -295,21 +308,12 @@
@Test
public void testUserSwitchedCallsOnUserSwitching() {
- mFakeFeatureFlags.set(Flags.LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE, true);
mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanging(mSecondaryUser.id,
mContext);
verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id);
}
@Test
- public void testUserSwitchedCallsOnUserSwitched() {
- mFakeFeatureFlags.set(Flags.LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE, false);
- mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanged(mSecondaryUser.id,
- mContext);
- verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id);
- }
-
- @Test
public void testIsLockscreenPublicMode() {
assertFalse(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id));
mLockscreenUserManager.setLockscreenPublicMode(true, mCurrentUser.id);
@@ -348,6 +352,262 @@
verify(listener, never()).onNotificationStateChanged();
}
+ @Test
+ public void testDevicePolicyDoesNotAllowNotifications() {
+ // User allows them
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ // DevicePolicy hides notifs on lockscreen
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
+ when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+ }
+
+ @Ignore("b/286230167")
+ @Test
+ public void testDevicePolicyDoesNotAllowNotifications_userAll() {
+ // User allows them
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ // DevicePolicy hides notifications
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
+ when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(USER_ALL));
+ }
+
+ @Test
+ @Ignore("b/286230167")
+ public void testDevicePolicyDoesNotAllowNotifications_secondary() {
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ // DevicePolicy hides notifications
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id))
+ .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
+ when(pr.getSendingUserId()).thenReturn(mSecondaryUser.id);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
+
+ // TODO (b/286230167): enable assertion
+ verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt());
+ }
+
+ @Test
+ public void testDevicePolicy_noPrivateNotifications() {
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ // DevicePolicy hides sensitive content
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
+ when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+
+ // TODO (b/286230167): enable assertion. It's currently called 4 times.
+ //verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt());
+ }
+
+ @Test
+ public void testDevicePolicy_noPrivateNotifications_userAll() {
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ // DevicePolicy hides sensitive content
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
+ when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ assertTrue(mLockscreenUserManager.needsRedaction(new NotificationEntryBuilder()
+ .setNotification(new Notification())
+ .setUser(UserHandle.ALL)
+ .build()));
+ }
+
+ @Test
+ public void testDevicePolicyPrivateNotifications_secondary() {
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ // DevicePolicy hides sensitive content
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id))
+ .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
+ when(pr.getSendingUserId()).thenReturn(mSecondaryUser.id);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+
+ // TODO (b/286230167): enable assertion. It's currently called 5 times.
+ //verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt());
+ }
+
+ @Test
+ public void testHideNotifications_primary() {
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+ }
+
+ @Test
+ public void testHideNotifications_secondary() {
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
+ }
+
+ @Ignore("b/286230167")
+ @Test
+ public void testHideNotifications_workProfile() {
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mWorkUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mWorkUser.id));
+ }
+
+ @Test
+ public void testHideNotifications_secondary_userSwitch() {
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id);
+
+ TestNotificationLockscreenUserManager lockscreenUserManager
+ = new TestNotificationLockscreenUserManager(mContext);
+ lockscreenUserManager.setUpWithPresenter(mPresenter);
+
+ lockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
+
+ assertFalse(lockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
+ }
+
+ @Test
+ public void testShowNotifications_secondary_userSwitch() {
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
+
+ TestNotificationLockscreenUserManager lockscreenUserManager
+ = new TestNotificationLockscreenUserManager(mContext);
+ lockscreenUserManager.setUpWithPresenter(mPresenter);
+
+ lockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
+
+ assertTrue(lockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
+ }
+
+ @Ignore("b/286230167")
+ @Test
+ public void testShouldShowLockscreenNotifications_keyguardManagerNoPrivateNotifications() {
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ // DevicePolicy allows notifications
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(0);
+ BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
+ when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ // KeyguardManager does not
+ when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
+
+ assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications());
+ }
+
+ @Test
+ public void testUserAllowsNotificationsInPublic_keyguardManagerNoPrivateNotifications() {
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ // DevicePolicy allows notifications
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(0);
+ BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
+ when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ // KeyguardManager does not
+ when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+ }
+
+ @Test
+ public void testUserAllowsNotificationsInPublic_settingsChange() {
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+
+ // User disables
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+ }
+
+ @Ignore("b/286230167")
+ @Test
+ public void testUserAllowsNotificationsInPublic_devicePolicyChange() {
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+
+ // DevicePolicy disables notifications
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+ BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
+ when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+ }
+
private class TestNotificationLockscreenUserManager
extends NotificationLockscreenUserManagerImpl {
public TestNotificationLockscreenUserManager(Context context) {
@@ -368,8 +628,7 @@
mKeyguardStateController,
mSettings,
mock(DumpManager.class),
- mock(LockPatternUtils.class),
- mFakeFeatureFlags);
+ mock(LockPatternUtils.class));
}
public BroadcastReceiver getBaseBroadcastReceiverForTest() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 1592c30..a6180ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -248,7 +248,7 @@
}
@Test
- public void testUpdateIconScale_smallerFontAndConstrainedDrawableSizeLessThanDpIconSize() {
+ public void testUpdateIconScale_smallerFontAndRawDrawableSizeLessThanDpIconSize() {
int dpIconSize = 60;
int dpDrawingSize = 30;
// smaller font scaling causes the spIconSize < dpIconSize
@@ -262,12 +262,42 @@
setIconDrawableWithSize(/* width= */ 50, /* height= */ 50);
mIconView.maybeUpdateIconScaleDimens();
- // WHEN both the constrained drawable width/height are less than dpIconSize,
+ // WHEN both the raw/constrained drawable width/height are less than dpIconSize,
+ // THEN the icon is scaled up from constrained drawable size to the raw drawable size
+ float scaleToBackRawDrawableSize = (float) 50 / 40;
// THEN the icon is scaled down from dpIconSize to fit the dpDrawingSize
float scaleToFitDrawingSize = (float) dpDrawingSize / dpIconSize;
// THEN the scaled icon should be scaled down further to fit spIconSize
float scaleToFitSpIconSize = (float) spIconSize / dpIconSize;
- assertEquals(scaleToFitDrawingSize * scaleToFitSpIconSize, mIconView.getIconScale(), 0.01f);
+ assertEquals(scaleToBackRawDrawableSize * scaleToFitDrawingSize * scaleToFitSpIconSize,
+ mIconView.getIconScale(), 0.01f);
+ }
+
+ @Test
+ public void testUpdateIconScale_smallerFontAndConstrainedDrawableSizeLessThanDpIconSize() {
+ int dpIconSize = 60;
+ int dpDrawingSize = 30;
+ // smaller font scaling causes the spIconSize < dpIconSize
+ int spIconSize = 40;
+ // the icon view layout size would be 40x150
+ // (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
+ setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
+ mIconView.setNotification(mock(StatusBarNotification.class));
+ // the raw drawable size is 70x70. When put the drawable into iconView whose
+ // layout size is 40x150, the drawable size would be constrained to 40x40
+ setIconDrawableWithSize(/* width= */ 70, /* height= */ 70);
+ mIconView.maybeUpdateIconScaleDimens();
+
+ // WHEN the raw drawable width/height are larger than dpIconSize,
+ // but the constrained drawable width/height are less than dpIconSize,
+ // THEN the icon is scaled up from constrained drawable size to fit dpIconSize
+ float scaleToFitDpIconSize = (float) dpIconSize / 40;
+ // THEN the icon is scaled down from dpIconSize to fit the dpDrawingSize
+ float scaleToFitDrawingSize = (float) dpDrawingSize / dpIconSize;
+ // THEN the scaled icon should be scaled down further to fit spIconSize
+ float scaleToFitSpIconSize = (float) spIconSize / dpIconSize;
+ assertEquals(scaleToFitDpIconSize * scaleToFitDrawingSize * scaleToFitSpIconSize,
+ mIconView.getIconScale(), 0.01f);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
index 2c8900c..61ba464 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
@@ -16,11 +16,18 @@
package com.android.systemui.statusbar.data.repository
+import com.android.systemui.statusbar.data.model.StatusBarAppearance
+import com.android.systemui.statusbar.data.model.StatusBarMode
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
import kotlinx.coroutines.flow.MutableStateFlow
class FakeStatusBarModeRepository : StatusBarModeRepository {
override val isTransientShown = MutableStateFlow(false)
override val isInFullscreenMode = MutableStateFlow(false)
+ override val statusBarAppearance = MutableStateFlow<StatusBarAppearance?>(null)
+ override val statusBarMode = MutableStateFlow(StatusBarMode.TRANSPARENT)
+
+ override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) {}
override fun showTransient() {
isTransientShown.value = true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index 27c1ba3..d1a46fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -16,20 +16,36 @@
package com.android.systemui.statusbar.data.repository
+import android.graphics.Rect
import android.view.WindowInsets
import android.view.WindowInsetsController
+import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
+import android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS
import android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS
+import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS
import androidx.test.filters.SmallTest
import com.android.internal.statusbar.LetterboxDetails
import com.android.internal.view.AppearanceRegion
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.data.model.StatusBarMode
+import com.android.systemui.statusbar.phone.BoundsPair
+import com.android.systemui.statusbar.phone.LetterboxAppearance
+import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
+import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
+import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.verify
@@ -37,14 +53,26 @@
class StatusBarModeRepositoryImplTest : SysuiTestCase() {
private val testScope = TestScope()
private val commandQueue = mock<CommandQueue>()
+ private val letterboxAppearanceCalculator = mock<LetterboxAppearanceCalculator>()
+ private val statusBarBoundsProvider = mock<StatusBarBoundsProvider>()
+ private val statusBarFragmentComponent =
+ mock<StatusBarFragmentComponent>().also {
+ whenever(it.boundsProvider).thenReturn(statusBarBoundsProvider)
+ }
+ private val ongoingCallRepository = OngoingCallRepository()
private val underTest =
StatusBarModeRepositoryImpl(
testScope.backgroundScope,
DISPLAY_ID,
commandQueue,
+ letterboxAppearanceCalculator,
+ ongoingCallRepository,
)
- .apply { this.start() }
+ .apply {
+ this.start()
+ this.onStatusBarViewInitialized(statusBarFragmentComponent)
+ }
private val commandQueueCallback: CommandQueue.Callbacks
get() {
@@ -53,6 +81,15 @@
return callbackCaptor.value
}
+ private val statusBarBoundsChangeListener: StatusBarBoundsProvider.BoundsChangeListener
+ get() {
+ val callbackCaptor = argumentCaptor<StatusBarBoundsProvider.BoundsChangeListener>()
+ verify(statusBarBoundsProvider).addChangeListener(capture(callbackCaptor))
+ return callbackCaptor.value
+ }
+
+ @Before fun setUp() {}
+
@Test
fun isTransientShown_commandQueueShow_wrongDisplayId_notUpdated() {
commandQueueCallback.showTransient(
@@ -228,6 +265,240 @@
assertThat(latest).isTrue()
}
+ @Test
+ fun statusBarAppearance_navBarColorManaged_matchesCallbackValue() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ onSystemBarAttributesChanged(navbarColorManagedByIme = true)
+
+ assertThat(latest!!.navbarColorManagedByIme).isTrue()
+
+ onSystemBarAttributesChanged(navbarColorManagedByIme = false)
+
+ assertThat(latest!!.navbarColorManagedByIme).isFalse()
+ }
+
+ @Test
+ fun statusBarAppearance_appearanceRegions_noLetterboxDetails_usesCallbackValues() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ whenever(
+ letterboxAppearanceCalculator.getLetterboxAppearance(
+ eq(APPEARANCE),
+ eq(APPEARANCE_REGIONS),
+ eq(LETTERBOX_DETAILS),
+ any(),
+ )
+ )
+ .thenReturn(CALCULATOR_LETTERBOX_APPEARANCE)
+
+ // WHEN the letterbox details are empty
+ onSystemBarAttributesChanged(
+ appearance = APPEARANCE,
+ appearanceRegions = APPEARANCE_REGIONS.toTypedArray(),
+ letterboxDetails = emptyArray(),
+ )
+
+ // THEN the appearance regions passed to the callback are used, *not*
+ // REGIONS_FROM_LETTERBOX_CALCULATOR
+ assertThat(latest!!.appearanceRegions).isEqualTo(APPEARANCE_REGIONS)
+ assertThat(latest!!.appearanceRegions).isNotEqualTo(REGIONS_FROM_LETTERBOX_CALCULATOR)
+ }
+
+ @Test
+ fun statusBarAppearance_appearanceRegions_letterboxDetails_usesLetterboxCalculator() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ whenever(
+ letterboxAppearanceCalculator.getLetterboxAppearance(
+ eq(APPEARANCE),
+ eq(APPEARANCE_REGIONS),
+ eq(LETTERBOX_DETAILS),
+ any(),
+ )
+ )
+ .thenReturn(CALCULATOR_LETTERBOX_APPEARANCE)
+
+ onSystemBarAttributesChanged(
+ appearance = APPEARANCE,
+ appearanceRegions = APPEARANCE_REGIONS.toTypedArray(),
+ letterboxDetails = LETTERBOX_DETAILS.toTypedArray(),
+ )
+
+ assertThat(latest!!.appearanceRegions).isEqualTo(REGIONS_FROM_LETTERBOX_CALCULATOR)
+ }
+
+ @Test
+ fun statusBarAppearance_boundsChanged_appearanceReFetched() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ // First, start with some appearances
+ val startingLetterboxAppearance =
+ LetterboxAppearance(
+ APPEARANCE_LIGHT_STATUS_BARS,
+ listOf(AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, Rect(0, 0, 1, 1)))
+ )
+ whenever(
+ letterboxAppearanceCalculator.getLetterboxAppearance(
+ eq(APPEARANCE),
+ eq(APPEARANCE_REGIONS),
+ eq(LETTERBOX_DETAILS),
+ any(),
+ )
+ )
+ .thenReturn(startingLetterboxAppearance)
+ onSystemBarAttributesChanged(
+ appearance = APPEARANCE,
+ appearanceRegions = APPEARANCE_REGIONS.toTypedArray(),
+ letterboxDetails = LETTERBOX_DETAILS.toTypedArray(),
+ )
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.TRANSPARENT)
+ assertThat(latest!!.appearanceRegions)
+ .isEqualTo(startingLetterboxAppearance.appearanceRegions)
+
+ // WHEN there's a new appearance and we get new status bar bounds
+ val newLetterboxAppearance =
+ LetterboxAppearance(
+ APPEARANCE_LOW_PROFILE_BARS,
+ listOf(AppearanceRegion(APPEARANCE_LOW_PROFILE_BARS, Rect(10, 20, 30, 40)))
+ )
+ whenever(
+ letterboxAppearanceCalculator.getLetterboxAppearance(
+ eq(APPEARANCE),
+ eq(APPEARANCE_REGIONS),
+ eq(LETTERBOX_DETAILS),
+ any(),
+ )
+ )
+ .thenReturn(newLetterboxAppearance)
+ statusBarBoundsChangeListener.onStatusBarBoundsChanged(
+ BoundsPair(Rect(0, 0, 50, 50), Rect(0, 0, 60, 60))
+ )
+
+ // THEN the new appearances are used
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.LIGHTS_OUT_TRANSPARENT)
+ assertThat(latest!!.appearanceRegions)
+ .isEqualTo(newLetterboxAppearance.appearanceRegions)
+ }
+
+ @Test
+ fun statusBarMode_ongoingCallAndFullscreen_semiTransparent() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ ongoingCallRepository.setHasOngoingCall(true)
+ onSystemBarAttributesChanged(
+ requestedVisibleTypes = WindowInsets.Type.navigationBars(),
+ )
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.SEMI_TRANSPARENT)
+ }
+
+ @Test
+ fun statusBarMode_ongoingCallButNotFullscreen_matchesAppearance() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ ongoingCallRepository.setHasOngoingCall(true)
+ onSystemBarAttributesChanged(
+ requestedVisibleTypes = WindowInsets.Type.statusBars(),
+ appearance = APPEARANCE_OPAQUE_STATUS_BARS,
+ )
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.OPAQUE)
+ }
+
+ @Test
+ fun statusBarMode_fullscreenButNotOngoingCall_matchesAppearance() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ ongoingCallRepository.setHasOngoingCall(false)
+ onSystemBarAttributesChanged(
+ requestedVisibleTypes = WindowInsets.Type.navigationBars(),
+ appearance = APPEARANCE_OPAQUE_STATUS_BARS,
+ )
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.OPAQUE)
+ }
+
+ @Test
+ fun statusBarMode_transientShown_semiTransparent() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+ onSystemBarAttributesChanged(
+ appearance = APPEARANCE_OPAQUE_STATUS_BARS,
+ )
+
+ underTest.showTransient()
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.SEMI_TRANSPARENT)
+ }
+
+ @Test
+ fun statusBarMode_appearanceLowProfileAndOpaque_lightsOut() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ onSystemBarAttributesChanged(
+ appearance = APPEARANCE_LOW_PROFILE_BARS or APPEARANCE_OPAQUE_STATUS_BARS,
+ )
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.LIGHTS_OUT)
+ }
+
+ @Test
+ fun statusBarMode_appearanceLowProfile_lightsOutTransparent() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ onSystemBarAttributesChanged(
+ appearance = APPEARANCE_LOW_PROFILE_BARS,
+ )
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.LIGHTS_OUT_TRANSPARENT)
+ }
+
+ @Test
+ fun statusBarMode_appearanceOpaque_opaque() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ onSystemBarAttributesChanged(
+ appearance = APPEARANCE_OPAQUE_STATUS_BARS,
+ )
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.OPAQUE)
+ }
+
+ @Test
+ fun statusBarMode_appearanceSemiTransparent_semiTransparent() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ onSystemBarAttributesChanged(
+ appearance = APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS,
+ )
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.SEMI_TRANSPARENT)
+ }
+
+ @Test
+ fun statusBarMode_appearanceNone_transparent() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ onSystemBarAttributesChanged(
+ appearance = 0,
+ )
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.TRANSPARENT)
+ }
+
private fun onSystemBarAttributesChanged(
displayId: Int = DISPLAY_ID,
@WindowInsetsController.Appearance appearance: Int = APPEARANCE_OPAQUE_STATUS_BARS,
@@ -253,5 +524,21 @@
private companion object {
const val DISPLAY_ID = 5
+ private const val APPEARANCE = APPEARANCE_LIGHT_STATUS_BARS
+ private val APPEARANCE_REGION = AppearanceRegion(APPEARANCE, Rect(0, 0, 150, 300))
+ private val APPEARANCE_REGIONS = listOf(APPEARANCE_REGION)
+ private val LETTERBOX_DETAILS =
+ listOf(
+ LetterboxDetails(
+ /* letterboxInnerBounds= */ Rect(0, 0, 10, 10),
+ /* letterboxFullBounds= */ Rect(0, 0, 20, 20),
+ /* appAppearance= */ 0
+ )
+ )
+ private val REGIONS_FROM_LETTERBOX_CALCULATOR =
+ listOf(AppearanceRegion(APPEARANCE, Rect(0, 0, 10, 20)))
+ private const val LETTERBOXED_APPEARANCE = APPEARANCE_LOW_PROFILE_BARS
+ private val CALCULATOR_LETTERBOX_APPEARANCE =
+ LetterboxAppearance(LETTERBOXED_APPEARANCE, REGIONS_FROM_LETTERBOX_CALCULATOR)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
new file mode 100644
index 0000000..8c3bfd5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
@@ -0,0 +1,254 @@
+/*
+ * 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.row.wrapper
+
+import android.app.PendingIntent
+import android.app.PendingIntent.CancelListener
+import android.content.Intent
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.testing.ViewUtils
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationTemplateViewWrapper.ActionPendingIntentCancellationHandler
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationTemplateViewWrapperTest : SysuiTestCase() {
+
+ private lateinit var helper: NotificationTestHelper
+
+ private lateinit var root: ViewGroup
+ private lateinit var view: ViewGroup
+ private lateinit var row: ExpandableNotificationRow
+ private lateinit var actions: ViewGroup
+
+ private lateinit var looper: TestableLooper
+
+ @Before
+ fun setUp() {
+ looper = TestableLooper.get(this)
+ allowTestableLooperAsMainThread()
+ helper = NotificationTestHelper(mContext, mDependency, looper)
+ row = helper.createRow()
+ // Some code in the view iterates through parents so we need some extra containers around
+ // it.
+ root = FrameLayout(mContext)
+ val root2 = FrameLayout(mContext)
+ root.addView(root2)
+ view =
+ (LayoutInflater.from(mContext)
+ .inflate(R.layout.notification_template_material_big_text, root2) as ViewGroup)
+ actions = view.findViewById(R.id.actions)!!
+ ViewUtils.attachView(root)
+ }
+
+ @Test
+ fun noActionsPresent_noCrash() {
+ view.removeView(actions)
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ wrapper.onContentUpdated(row)
+ }
+
+ @Test
+ fun actionPendingIntentCancelled_actionDisabled() {
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ val action1 = createActionWithPendingIntent()
+ val action2 = createActionWithPendingIntent()
+ val action3 = createActionWithPendingIntent()
+ wrapper.onContentUpdated(row)
+ waitForUiOffloadThread() // Wait for cancellation registration to execute.
+
+ val pi3 = getPendingIntent(action3)
+ pi3.cancel()
+ looper.processAllMessages() // Wait for listener callbacks to execute
+
+ assertThat(action1.isEnabled).isTrue()
+ assertThat(action2.isEnabled).isTrue()
+ assertThat(action3.isEnabled).isFalse()
+ assertThat(wrapper.mCancelledPendingIntents)
+ .doesNotContain(getPendingIntent(action1).hashCode())
+ assertThat(wrapper.mCancelledPendingIntents)
+ .doesNotContain(getPendingIntent(action2).hashCode())
+ assertThat(wrapper.mCancelledPendingIntents).contains(pi3.hashCode())
+ }
+
+ @Test
+ fun newActionWithSamePendingIntentPosted_actionDisabled() {
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ val action = createActionWithPendingIntent()
+ wrapper.onContentUpdated(row)
+ waitForUiOffloadThread() // Wait for cancellation registration to execute.
+
+ // Cancel the intent and check action is now false.
+ val pi = getPendingIntent(action)
+ pi.cancel()
+ looper.processAllMessages() // Wait for listener callbacks to execute
+ assertThat(action.isEnabled).isFalse()
+
+ // Create a NEW action and make sure that one will also be cancelled with same PI.
+ actions.removeView(action)
+ val newAction = createActionWithPendingIntent(pi)
+ wrapper.onContentUpdated(row)
+ looper.processAllMessages() // Wait for listener callbacks to execute
+
+ assertThat(newAction.isEnabled).isFalse()
+ assertThat(wrapper.mCancelledPendingIntents).containsExactly(pi.hashCode())
+ }
+
+ @Test
+ fun twoActionsWithSameCancelledIntent_bothActionsDisabled() {
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ val action1 = createActionWithPendingIntent()
+ val action2 = createActionWithPendingIntent()
+ val action3 = createActionWithPendingIntent(getPendingIntent(action2))
+ wrapper.onContentUpdated(row)
+ waitForUiOffloadThread() // Wait for cancellation registration to execute.
+
+ val pi = getPendingIntent(action2)
+ pi.cancel()
+ looper.processAllMessages() // Wait for listener callbacks to execute
+
+ assertThat(action1.isEnabled).isTrue()
+ assertThat(action2.isEnabled).isFalse()
+ assertThat(action3.isEnabled).isFalse()
+ }
+
+ @Test
+ fun actionPendingIntentCancelled_whileDetached_actionDisabled() {
+ ViewUtils.detachView(root)
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ val action = createActionWithPendingIntent()
+ wrapper.onContentUpdated(row)
+ getPendingIntent(action).cancel()
+ ViewUtils.attachView(root)
+ waitForUiOffloadThread()
+ looper.processAllMessages()
+
+ assertThat(action.isEnabled).isFalse()
+ }
+
+ @Test
+ fun actionViewDetached_pendingIntentListenersDeregistered() {
+ val pi =
+ PendingIntent.getActivity(
+ mContext,
+ System.currentTimeMillis().toInt(),
+ Intent(Intent.ACTION_VIEW),
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ val spy = Mockito.spy(pi)
+ createActionWithPendingIntent(spy)
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ wrapper.onContentUpdated(row)
+ ViewUtils.detachView(root)
+ waitForUiOffloadThread()
+ looper.processAllMessages()
+
+ val captor = ArgumentCaptor.forClass(CancelListener::class.java)
+ verify(spy, times(1)).registerCancelListener(captor.capture())
+ verify(spy, times(1)).unregisterCancelListener(captor.value)
+ }
+
+ @Test
+ fun actionViewUpdated_oldPendingIntentListenersRemoved() {
+ val pi =
+ PendingIntent.getActivity(
+ mContext,
+ System.currentTimeMillis().toInt(),
+ Intent(Intent.ACTION_VIEW),
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ val spy = Mockito.spy(pi)
+ val action = createActionWithPendingIntent(spy)
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ wrapper.onContentUpdated(row)
+ waitForUiOffloadThread()
+ looper.processAllMessages()
+
+ // Grab set attach listener
+ val attachListener =
+ Mockito.spy(action.getTag(com.android.systemui.res.R.id.pending_intent_listener_tag))
+ as ActionPendingIntentCancellationHandler
+ action.setTag(com.android.systemui.res.R.id.pending_intent_listener_tag, attachListener)
+
+ // Update pending intent in the existing action
+ val newPi =
+ PendingIntent.getActivity(
+ mContext,
+ System.currentTimeMillis().toInt(),
+ Intent(Intent.ACTION_ALARM_CHANGED),
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ action.setTagInternal(R.id.pending_intent_tag, newPi)
+ wrapper.onContentUpdated(row)
+ waitForUiOffloadThread()
+ looper.processAllMessages()
+
+ // Listeners for original pending intent need to be cleaned up now.
+ val captor = ArgumentCaptor.forClass(CancelListener::class.java)
+ verify(spy, times(1)).registerCancelListener(captor.capture())
+ verify(spy, times(1)).unregisterCancelListener(captor.value)
+ // Attach listener has to be replaced with a new one.
+ assertThat(action.getTag(com.android.systemui.res.R.id.pending_intent_listener_tag))
+ .isNotEqualTo(attachListener)
+ verify(attachListener).remove()
+ }
+
+ private fun createActionWithPendingIntent(): View {
+ val pi =
+ PendingIntent.getActivity(
+ mContext,
+ System.currentTimeMillis().toInt(),
+ Intent(Intent.ACTION_VIEW),
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ return createActionWithPendingIntent(pi)
+ }
+
+ private fun createActionWithPendingIntent(pi: PendingIntent): View {
+ val view =
+ LayoutInflater.from(mContext)
+ .inflate(R.layout.notification_material_action, null, false)
+ view.setTagInternal(R.id.pending_intent_tag, pi)
+ actions.addView(view)
+ return view
+ }
+
+ private fun getPendingIntent(action: View): PendingIntent {
+ val pendingIntent = action.getTag(R.id.pending_intent_tag) as PendingIntent
+ assertThat(pendingIntent).isNotNull()
+ return pendingIntent
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index c71c0c57..a5d3484 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -24,7 +24,6 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -35,14 +34,11 @@
import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
import android.view.HapticFeedbackConstants;
-import android.view.WindowInsets;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.testing.FakeMetricsLogger;
-import com.android.internal.statusbar.LetterboxDetails;
-import com.android.internal.view.AppearanceRegion;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
@@ -100,7 +96,6 @@
@Mock private VibratorHelper mVibratorHelper;
@Mock private Vibrator mVibrator;
@Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
- @Mock private SystemBarAttributesListener mSystemBarAttributesListener;
@Mock private Lazy<CameraLauncher> mCameraLauncherLazy;
@Mock private UserTracker mUserTracker;
@Mock private QSHost mQSHost;
@@ -139,7 +134,6 @@
Optional.of(mVibrator),
new DisableFlagsLogger(),
DEFAULT_DISPLAY,
- mSystemBarAttributesListener,
mCameraLauncherLazy,
mUserTracker,
mQSHost,
@@ -197,62 +191,6 @@
}
@Test
- public void onSystemBarAttributesChanged_forwardsToSysBarAttrsListener() {
- int displayId = DEFAULT_DISPLAY;
- int appearance = 123;
- AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{};
- boolean navbarColorManagedByIme = true;
- int behavior = 456;
- int requestedVisibleTypes = WindowInsets.Type.systemBars();
- String packageName = "test package name";
- LetterboxDetails[] letterboxDetails = new LetterboxDetails[]{};
-
- mSbcqCallbacks.onSystemBarAttributesChanged(
- displayId,
- appearance,
- appearanceRegions,
- navbarColorManagedByIme,
- behavior,
- requestedVisibleTypes,
- packageName,
- letterboxDetails);
-
- verify(mSystemBarAttributesListener).onSystemBarAttributesChanged(
- displayId,
- appearance,
- appearanceRegions,
- navbarColorManagedByIme,
- behavior,
- requestedVisibleTypes,
- packageName,
- letterboxDetails
- );
- }
-
- @Test
- public void onSystemBarAttributesChanged_differentDisplayId_doesNotForwardToAttrsListener() {
- int appearance = 123;
- AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{};
- boolean navbarColorManagedByIme = true;
- int behavior = 456;
- int requestedVisibleTypes = WindowInsets.Type.systemBars();
- String packageName = "test package name";
- LetterboxDetails[] letterboxDetails = new LetterboxDetails[]{};
-
- mSbcqCallbacks.onSystemBarAttributesChanged(
- DEFAULT_DISPLAY + 1,
- appearance,
- appearanceRegions,
- navbarColorManagedByIme,
- behavior,
- requestedVisibleTypes,
- packageName,
- letterboxDetails);
-
- verifyZeroInteractions(mSystemBarAttributesListener);
- }
-
- @Test
public void vibrateOnNavigationKeyDown_oneWayHapticsDisabled_usesVibrate() {
mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 0b171b2..e33fa22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -165,7 +165,6 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -295,7 +294,6 @@
@Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
@Mock private BrightnessSliderController.Factory mBrightnessSliderFactory;
@Mock private WallpaperController mWallpaperController;
- @Mock private OngoingCallController mOngoingCallController;
@Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
@Mock private LockscreenShadeTransitionController mLockscreenTransitionController;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@@ -535,7 +533,6 @@
mBrightnessSliderFactory,
mScreenOffAnimationController,
mWallpaperController,
- mOngoingCallController,
mStatusBarHideIconsForBouncerManager,
mLockscreenTransitionController,
mFeatureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
index b2dc0dc..e7b287c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
@@ -26,7 +26,6 @@
import com.android.internal.view.AppearanceRegion
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -47,15 +46,12 @@
private val TEST_APPEARANCE_REGION_BOUNDS = Rect(0, 0, 20, 100)
private val TEST_APPEARANCE_REGION =
AppearanceRegion(TEST_APPEARANCE, TEST_APPEARANCE_REGION_BOUNDS)
- private val TEST_APPEARANCE_REGIONS = arrayOf(TEST_APPEARANCE_REGION)
+ private val TEST_APPEARANCE_REGIONS = listOf(TEST_APPEARANCE_REGION)
private val TEST_WINDOW_BOUNDS = Rect(0, 0, 500, 500)
}
@get:Rule var expect = Expect.create()
- @Mock private lateinit var lightBarController: LightBarController
- @Mock private lateinit var statusBarBoundsProvider: StatusBarBoundsProvider
- @Mock private lateinit var statusBarFragmentComponent: StatusBarFragmentComponent
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var letterboxBackgroundProvider: LetterboxBackgroundProvider
@@ -64,24 +60,21 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(statusBarFragmentComponent.boundsProvider).thenReturn(statusBarBoundsProvider)
calculator =
- LetterboxAppearanceCalculator(
- lightBarController, dumpManager, letterboxBackgroundProvider)
- calculator.onStatusBarViewInitialized(statusBarFragmentComponent)
+ LetterboxAppearanceCalculator(context, dumpManager, letterboxBackgroundProvider)
whenever(letterboxBackgroundProvider.letterboxBackgroundColor).thenReturn(Color.BLACK)
whenever(letterboxBackgroundProvider.isLetterboxBackgroundMultiColored).thenReturn(false)
}
@Test
fun getLetterboxAppearance_overlapStartSide_returnsOriginalWithScrim() {
- whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100))
- whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100))
+ val start = Rect(0, 0, 100, 100)
+ val end = Rect(200, 0, 300, 100)
val letterbox = letterboxWithInnerBounds(Rect(50, 50, 150, 150))
val letterboxAppearance =
calculator.getLetterboxAppearance(
- TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox))
+ TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
expect
.that(letterboxAppearance.appearance)
@@ -91,13 +84,13 @@
@Test
fun getLetterboxAppearance_overlapEndSide_returnsOriginalWithScrim() {
- whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100))
- whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100))
+ val start = Rect(0, 0, 100, 100)
+ val end = Rect(200, 0, 300, 100)
val letterbox = letterboxWithInnerBounds(Rect(150, 50, 250, 150))
val letterboxAppearance =
calculator.getLetterboxAppearance(
- TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox))
+ TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
expect
.that(letterboxAppearance.appearance)
@@ -114,14 +107,12 @@
val statusBarStartSideBoundsCopy = Rect(statusBarStartSideBounds)
val statusBarEndSideBoundsCopy = Rect(statusBarEndSideBounds)
val letterBoxInnerBoundsCopy = Rect(letterBoxInnerBounds)
- whenever(statusBarBoundsProvider.visibleStartSideBounds)
- .thenReturn(statusBarStartSideBounds)
- whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(statusBarEndSideBounds)
calculator.getLetterboxAppearance(
TEST_APPEARANCE,
TEST_APPEARANCE_REGIONS,
- arrayOf(letterboxWithInnerBounds(letterBoxInnerBounds))
+ listOf(letterboxWithInnerBounds(letterBoxInnerBounds)),
+ BoundsPair(statusBarStartSideBounds, statusBarEndSideBounds)
)
expect.that(statusBarStartSideBounds).isEqualTo(statusBarStartSideBoundsCopy)
@@ -132,13 +123,13 @@
@Test
fun getLetterboxAppearance_noOverlap_BackgroundMultiColor_returnsAppearanceWithScrim() {
whenever(letterboxBackgroundProvider.isLetterboxBackgroundMultiColored).thenReturn(true)
- whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100))
- whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100))
+ val start = Rect(0, 0, 100, 100)
+ val end = Rect(200, 0, 300, 100)
val letterbox = letterboxWithInnerBounds(Rect(101, 0, 199, 100))
val letterboxAppearance =
calculator.getLetterboxAppearance(
- TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox))
+ TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
expect
.that(letterboxAppearance.appearance)
@@ -148,66 +139,66 @@
@Test
fun getLetterboxAppearance_noOverlap_returnsAppearanceWithoutScrim() {
- whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100))
- whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100))
+ val start = Rect(0, 0, 100, 100)
+ val end = Rect(200, 0, 300, 100)
val letterbox = letterboxWithInnerBounds(Rect(101, 0, 199, 100))
val letterboxAppearance =
calculator.getLetterboxAppearance(
- TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox))
+ TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE)
}
@Test
fun getLetterboxAppearance_letterboxContainsStartSide_returnsAppearanceWithoutScrim() {
- whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100))
- whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100))
+ val start = Rect(0, 0, 100, 100)
+ val end = Rect(200, 0, 300, 100)
val letterbox = letterboxWithInnerBounds(Rect(0, 0, 101, 101))
val letterboxAppearance =
calculator.getLetterboxAppearance(
- TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox))
+ TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE)
}
@Test
fun getLetterboxAppearance_letterboxContainsEndSide_returnsAppearanceWithoutScrim() {
- whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100))
- whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100))
+ val start = Rect(0, 0, 100, 100)
+ val end = Rect(200, 0, 300, 100)
val letterbox = letterboxWithInnerBounds(Rect(199, 0, 301, 101))
val letterboxAppearance =
calculator.getLetterboxAppearance(
- TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox))
+ TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE)
}
@Test
fun getLetterboxAppearance_letterboxContainsEntireStatusBar_returnsAppearanceWithoutScrim() {
- whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100))
- whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100))
+ val start = Rect(0, 0, 100, 100)
+ val end = Rect(200, 0, 300, 100)
val letterbox = letterboxWithInnerBounds(Rect(0, 0, 300, 100))
val letterboxAppearance =
calculator.getLetterboxAppearance(
- TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox))
+ TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE)
}
@Test
fun getLetterboxAppearance_returnsAdaptedAppearanceRegions_basedOnLetterboxInnerBounds() {
- whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 0, 0))
- whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(0, 0, 0, 0))
+ val start = Rect(0, 0, 0, 0)
+ val end = Rect(0, 0, 0, 0)
val letterbox = letterboxWithInnerBounds(Rect(150, 0, 300, 800))
val letterboxRegion = TEST_APPEARANCE_REGION.copy(bounds = letterbox.letterboxFullBounds)
val letterboxAppearance =
calculator.getLetterboxAppearance(
- TEST_APPEARANCE, arrayOf(letterboxRegion), arrayOf(letterbox))
+ TEST_APPEARANCE, listOf(letterboxRegion), listOf(letterbox), BoundsPair(start, end))
val letterboxAdaptedRegion = letterboxRegion.copy(bounds = letterbox.letterboxInnerBounds)
assertThat(letterboxAppearance.appearanceRegions.toList()).contains(letterboxAdaptedRegion)
@@ -216,8 +207,8 @@
@Test
fun getLetterboxAppearance_returnsDefaultAppearanceRegions_basedOnLetterboxOuterBounds() {
- whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 0, 0))
- whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(0, 0, 0, 0))
+ val start = Rect(0, 0, 0, 0)
+ val end = Rect(0, 0, 0, 0)
val letterbox =
letterboxWithBounds(
innerBounds = Rect(left = 25, top = 0, right = 75, bottom = 100),
@@ -226,16 +217,20 @@
val letterboxAppearance =
calculator.getLetterboxAppearance(
- TEST_APPEARANCE, arrayOf(letterboxRegion), arrayOf(letterbox))
+ TEST_APPEARANCE, listOf(letterboxRegion), listOf(letterbox), BoundsPair(start, end))
val outerRegions =
listOf(
AppearanceRegion(
- DEFAULT_APPEARANCE, Rect(left = 0, top = 0, right = 25, bottom = 100)),
+ DEFAULT_APPEARANCE,
+ Rect(left = 0, top = 0, right = 25, bottom = 100),
+ ),
AppearanceRegion(
- DEFAULT_APPEARANCE, Rect(left = 75, top = 0, right = 100, bottom = 100)),
+ DEFAULT_APPEARANCE,
+ Rect(left = 75, top = 0, right = 100, bottom = 100),
+ ),
)
- assertThat(letterboxAppearance.appearanceRegions.toList())
+ assertThat(letterboxAppearance.appearanceRegions)
.containsAtLeastElementsIn(outerRegions)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
index df3c1e5..c45ecf3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
@@ -22,12 +22,14 @@
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -42,11 +44,16 @@
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.view.AppearanceRegion;
+import com.android.keyguard.TestScopeProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.settings.FakeDisplayTracker;
+import com.android.systemui.statusbar.data.model.StatusBarAppearance;
+import com.android.systemui.statusbar.data.model.StatusBarMode;
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.util.kotlin.JavaAdapter;
import org.junit.Before;
import org.junit.Test;
@@ -54,6 +61,10 @@
import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import kotlinx.coroutines.test.TestScope;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -62,10 +73,16 @@
private static final GradientColors COLORS_LIGHT = makeColors(Color.WHITE);
private static final GradientColors COLORS_DARK = makeColors(Color.BLACK);
+ private static final BoundsPair STATUS_BAR_BOUNDS = new BoundsPair(
+ /* start= */ new Rect(0, 0, 10, 10),
+ /* end= */ new Rect(0, 0, 20, 20));
private LightBarTransitionsController mLightBarTransitionsController;
private LightBarTransitionsController mNavBarController;
private SysuiDarkIconDispatcher mStatusBarIconController;
private LightBarController mLightBarController;
+ private final TestScope mTestScope = TestScopeProvider.getTestScope();
+ private final FakeStatusBarModeRepository mStatusBarModeRepository =
+ new FakeStatusBarModeRepository();
@Before
public void setup() {
@@ -77,11 +94,14 @@
mLightBarTransitionsController);
mLightBarController = new LightBarController(
mContext,
+ new JavaAdapter(mTestScope),
mStatusBarIconController,
mock(BatteryController.class),
mock(NavigationModeController.class),
+ mStatusBarModeRepository,
mock(DumpManager.class),
new FakeDisplayTracker(mContext));
+ mLightBarController.start();
}
private static GradientColors makeColors(@ColorInt int bgColor) {
@@ -96,13 +116,19 @@
public void testOnStatusBarAppearanceChanged_multipleStacks_allStacksLight() {
final Rect firstBounds = new Rect(0, 0, 1, 1);
final Rect secondBounds = new Rect(1, 0, 2, 1);
- final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+ final List<AppearanceRegion> appearanceRegions = Arrays.asList(
new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, firstBounds),
new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, secondBounds)
- };
- mLightBarController.onStatusBarAppearanceChanged(
- appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
- false /* navbarColorManagedByIme */);
+ );
+
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
+ new StatusBarAppearance(
+ StatusBarMode.TRANSPARENT,
+ STATUS_BAR_BOUNDS,
+ appearanceRegions,
+ /* navbarColorManagedByIme= */ false));
+ mTestScope.getTestScheduler().advanceUntilIdle();
+
verify(mStatusBarIconController).setIconsDarkArea(eq(null));
verify(mLightBarTransitionsController).setIconsDark(eq(true), anyBoolean());
}
@@ -111,13 +137,19 @@
public void testOnStatusBarAppearanceChanged_multipleStacks_oneStackLightOneStackDark() {
final Rect firstBounds = new Rect(0, 0, 1, 1);
final Rect secondBounds = new Rect(1, 0, 2, 1);
- final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+ final List<AppearanceRegion> appearanceRegions = Arrays.asList(
new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, firstBounds),
new AppearanceRegion(0 /* appearance */, secondBounds)
- };
- mLightBarController.onStatusBarAppearanceChanged(
- appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
- false /* navbarColorManagedByIme */);
+ );
+
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
+ new StatusBarAppearance(
+ StatusBarMode.TRANSPARENT,
+ STATUS_BAR_BOUNDS,
+ appearanceRegions,
+ /* navbarColorManagedByIme= */ false));
+ mTestScope.getTestScheduler().advanceUntilIdle();
+
ArgumentCaptor<ArrayList<Rect>> captor = ArgumentCaptor.forClass(ArrayList.class);
verify(mStatusBarIconController).setIconsDarkArea(captor.capture());
assertTrue(captor.getValue().contains(firstBounds));
@@ -128,13 +160,19 @@
public void testOnStatusBarAppearanceChanged_multipleStacks_oneStackDarkOneStackLight() {
final Rect firstBounds = new Rect(0, 0, 1, 1);
final Rect secondBounds = new Rect(1, 0, 2, 1);
- final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+ final List<AppearanceRegion> appearanceRegions = Arrays.asList(
new AppearanceRegion(0 /* appearance */, firstBounds),
new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, secondBounds)
- };
- mLightBarController.onStatusBarAppearanceChanged(
- appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
- false /* navbarColorManagedByIme */);
+ );
+
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
+ new StatusBarAppearance(
+ StatusBarMode.TRANSPARENT,
+ STATUS_BAR_BOUNDS,
+ appearanceRegions,
+ /* navbarColorManagedByIme= */ false));
+ mTestScope.getTestScheduler().advanceUntilIdle();
+
ArgumentCaptor<ArrayList<Rect>> captor = ArgumentCaptor.forClass(ArrayList.class);
verify(mStatusBarIconController).setIconsDarkArea(captor.capture());
assertTrue(captor.getValue().contains(secondBounds));
@@ -146,14 +184,20 @@
final Rect firstBounds = new Rect(0, 0, 1, 1);
final Rect secondBounds = new Rect(1, 0, 2, 1);
final Rect thirdBounds = new Rect(2, 0, 3, 1);
- final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+ final List<AppearanceRegion> appearanceRegions = Arrays.asList(
new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, firstBounds),
new AppearanceRegion(0 /* appearance */, secondBounds),
new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, thirdBounds)
- };
- mLightBarController.onStatusBarAppearanceChanged(
- appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
- false /* navbarColorManagedByIme */);
+ );
+
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
+ new StatusBarAppearance(
+ StatusBarMode.TRANSPARENT,
+ STATUS_BAR_BOUNDS,
+ appearanceRegions,
+ /* navbarColorManagedByIme= */ false));
+ mTestScope.getTestScheduler().advanceUntilIdle();
+
ArgumentCaptor<ArrayList<Rect>> captor = ArgumentCaptor.forClass(ArrayList.class);
verify(mStatusBarIconController).setIconsDarkArea(captor.capture());
assertTrue(captor.getValue().contains(firstBounds));
@@ -165,40 +209,121 @@
public void testOnStatusBarAppearanceChanged_multipleStacks_allStacksDark() {
final Rect firstBounds = new Rect(0, 0, 1, 1);
final Rect secondBounds = new Rect(1, 0, 2, 1);
- final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+ final List<AppearanceRegion> appearanceRegions = Arrays.asList(
new AppearanceRegion(0 /* appearance */, firstBounds),
new AppearanceRegion(0 /* appearance */, secondBounds)
- };
- mLightBarController.onStatusBarAppearanceChanged(
- appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
- false /* navbarColorManagedByIme */);
+ );
+
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
+ new StatusBarAppearance(
+ StatusBarMode.TRANSPARENT,
+ STATUS_BAR_BOUNDS,
+ appearanceRegions,
+ /* navbarColorManagedByIme= */ false));
+ mTestScope.getTestScheduler().advanceUntilIdle();
+
verify(mLightBarTransitionsController).setIconsDark(eq(false), anyBoolean());
}
@Test
public void testOnStatusBarAppearanceChanged_singleStack_light() {
- final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+ final List<AppearanceRegion> appearanceRegions = List.of(
new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, new Rect(0, 0, 1, 1))
- };
- mLightBarController.onStatusBarAppearanceChanged(
- appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
- false /* navbarColorManagedByIme */);
+ );
+
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
+ new StatusBarAppearance(
+ StatusBarMode.TRANSPARENT,
+ STATUS_BAR_BOUNDS,
+ appearanceRegions,
+ /* navbarColorManagedByIme= */ false));
+ mTestScope.getTestScheduler().advanceUntilIdle();
+
verify(mStatusBarIconController).setIconsDarkArea(eq(null));
verify(mLightBarTransitionsController).setIconsDark(eq(true), anyBoolean());
}
@Test
public void testOnStatusBarAppearanceChanged_singleStack_dark() {
- final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+ final List<AppearanceRegion> appearanceRegions = List.of(
new AppearanceRegion(0, new Rect(0, 0, 1, 1))
- };
- mLightBarController.onStatusBarAppearanceChanged(
- appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
- false /* navbarColorManagedByIme */);
+ );
+
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
+ new StatusBarAppearance(
+ StatusBarMode.TRANSPARENT,
+ STATUS_BAR_BOUNDS,
+ appearanceRegions,
+ /* navbarColorManagedByIme= */ false));
+ mTestScope.getTestScheduler().advanceUntilIdle();
+
verify(mLightBarTransitionsController).setIconsDark(eq(false), anyBoolean());
}
@Test
+ public void testOnStatusBarAppearanceChanged_statusBarModeChanged_statusRedrawn() {
+ final List<AppearanceRegion> appearanceRegions = List.of(
+ new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, new Rect(0, 0, 1, 1))
+ );
+
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
+ new StatusBarAppearance(
+ StatusBarMode.TRANSPARENT,
+ STATUS_BAR_BOUNDS,
+ appearanceRegions,
+ /* navbarColorManagedByIme= */ false));
+ mTestScope.getTestScheduler().advanceUntilIdle();
+ reset(mStatusBarIconController);
+
+ // WHEN the same appearance regions but different status bar mode is sent
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
+ new StatusBarAppearance(
+ StatusBarMode.LIGHTS_OUT_TRANSPARENT,
+ STATUS_BAR_BOUNDS,
+ appearanceRegions,
+ /* navbarColorManagedByIme= */ false));
+ mTestScope.getTestScheduler().advanceUntilIdle();
+
+ // THEN the StatusBarIconController gets a redraw request
+ verify(mStatusBarIconController).setIconsDarkArea(any());
+ }
+
+ /** Regression test for b/301605450. */
+ @Test
+ public void testOnStatusBarAppearanceChanged_statusBarBoundsChanged_statusRedrawn() {
+ final List<AppearanceRegion> appearanceRegions = List.of(
+ new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, new Rect(0, 0, 1, 1))
+ );
+ BoundsPair startingBounds = new BoundsPair(
+ /* start= */ new Rect(0, 0, 10, 10),
+ /* end= */ new Rect(0, 0, 20, 20));
+
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
+ new StatusBarAppearance(
+ StatusBarMode.TRANSPARENT,
+ startingBounds,
+ appearanceRegions,
+ /* navbarColorManagedByIme= */ false));
+ mTestScope.getTestScheduler().advanceUntilIdle();
+ reset(mStatusBarIconController);
+
+ // WHEN the same appearance regions but different status bar bounds are sent
+ BoundsPair newBounds = new BoundsPair(
+ /* start= */ new Rect(0, 0, 30, 30),
+ /* end= */ new Rect(0, 0, 40, 40));
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
+ new StatusBarAppearance(
+ StatusBarMode.TRANSPARENT,
+ newBounds,
+ appearanceRegions,
+ /* navbarColorManagedByIme= */ false));
+ mTestScope.getTestScheduler().advanceUntilIdle();
+
+ // THEN the StatusBarIconController gets a redraw request
+ verify(mStatusBarIconController).setIconsDarkArea(any());
+ }
+
+ @Test
public void validateNavBarChangesUpdateIcons() {
// On the launcher in dark mode buttons are light
mLightBarController.setScrimState(ScrimState.UNLOCKED, 0f, COLORS_DARK);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt
index d84010d..61da701 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt
@@ -24,14 +24,15 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider.BoundsChangeListener
+import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.any
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -60,8 +61,9 @@
startSideContent = spy(FrameLayout(context)).apply { setBoundsOnScreen(START_SIDE_BOUNDS) }
endSideContent = spy(FrameLayout(context)).apply { setBoundsOnScreen(END_SIDE_BOUNDS) }
- boundsProvider =
- StatusBarBoundsProvider(setOf(boundsChangeListener), startSideContent, endSideContent)
+ boundsProvider = StatusBarBoundsProvider(startSideContent, endSideContent)
+ boundsProvider.addChangeListener(boundsChangeListener)
+ reset(boundsChangeListener)
}
@Test
@@ -81,7 +83,7 @@
startSideContent.setBoundsOnScreen(newBounds)
- verify(boundsChangeListener).onStatusBarBoundsChanged()
+ verify(boundsChangeListener).onStatusBarBoundsChanged(any())
}
@Test
@@ -90,7 +92,7 @@
startSideContent.setBoundsOnScreen(newBounds)
- verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
+ verify(boundsChangeListener, never()).onStatusBarBoundsChanged(any())
}
@Test
@@ -101,7 +103,7 @@
startSideContent.setBoundsOnScreen(newBounds)
- verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
+ verify(boundsChangeListener, never()).onStatusBarBoundsChanged(any())
}
@Test
@@ -111,7 +113,7 @@
startSideContent.layout(newBounds)
- verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
+ verify(boundsChangeListener, never()).onStatusBarBoundsChanged(any())
}
@Test
@@ -121,7 +123,7 @@
endSideContent.setBoundsOnScreen(newBounds)
- verify(boundsChangeListener).onStatusBarBoundsChanged()
+ verify(boundsChangeListener).onStatusBarBoundsChanged(any())
}
@Test
@@ -130,7 +132,7 @@
endSideContent.setBoundsOnScreen(newBounds)
- verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
+ verify(boundsChangeListener, never()).onStatusBarBoundsChanged(any())
}
@Test
@@ -141,7 +143,7 @@
endSideContent.setBoundsOnScreen(newBounds)
- verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
+ verify(boundsChangeListener, never()).onStatusBarBoundsChanged(any())
}
@Test
@@ -151,7 +153,7 @@
endSideContent.layout(newBounds)
- verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
+ verify(boundsChangeListener, never()).onStatusBarBoundsChanged(any())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
deleted file mode 100644
index 0cd2214..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
+++ /dev/null
@@ -1,187 +0,0 @@
-package com.android.systemui.statusbar.phone
-
-import android.graphics.Rect
-import android.testing.AndroidTestingRunner
-import android.view.Display
-import android.view.WindowInsets
-import android.view.WindowInsetsController
-import android.view.WindowInsetsController.*
-import androidx.test.filters.SmallTest
-import com.android.internal.statusbar.LetterboxDetails
-import com.android.internal.view.AppearanceRegion
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when` as whenever
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class SystemBarAttributesListenerTest : SysuiTestCase() {
-
- @Mock private lateinit var dumpManager: DumpManager
- @Mock private lateinit var lightBarController: LightBarController
- @Mock private lateinit var letterboxAppearanceCalculator: LetterboxAppearanceCalculator
- @Mock private lateinit var centralSurfaces: CentralSurfaces
-
- private lateinit var sysBarAttrsListener: SystemBarAttributesListener
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- whenever(
- letterboxAppearanceCalculator.getLetterboxAppearance(
- anyInt(), anyObject(), anyObject()))
- .thenReturn(TEST_LETTERBOX_APPEARANCE)
-
- sysBarAttrsListener =
- SystemBarAttributesListener(
- centralSurfaces,
- letterboxAppearanceCalculator,
- lightBarController,
- dumpManager)
- }
-
- @Test
- fun onSysBarAttrsChanged_forwardsAppearanceToCentralSurfaces() {
- val appearance = APPEARANCE_LIGHT_STATUS_BARS or APPEARANCE_LIGHT_NAVIGATION_BARS
-
- changeSysBarAttrs(appearance)
-
- verify(centralSurfaces).setAppearance(appearance)
- }
-
- @Test
- fun onSysBarAttrsChanged_forwardsLetterboxAppearanceToCentralSurfaces() {
- changeSysBarAttrs(TEST_APPEARANCE, TEST_LETTERBOX_DETAILS)
-
- verify(centralSurfaces).setAppearance(TEST_LETTERBOX_APPEARANCE.appearance)
- }
-
- @Test
- fun onSysBarAttrsChanged_noLetterbox_forwardsOriginalAppearanceToCtrlSrfcs() {
- changeSysBarAttrs(TEST_APPEARANCE, arrayOf<LetterboxDetails>())
-
- verify(centralSurfaces).setAppearance(TEST_APPEARANCE)
- }
-
- @Test
- fun onSysBarAttrsChanged_forwardsAppearanceToLightBarController() {
- changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS)
-
- verify(lightBarController)
- .onStatusBarAppearanceChanged(
- eq(TEST_APPEARANCE_REGIONS), anyBoolean(), anyInt(), anyBoolean())
- }
-
- @Test
- fun onSysBarAttrsChanged_forwardsLetterboxAppearanceToLightBarController() {
- changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
-
- verify(lightBarController)
- .onStatusBarAppearanceChanged(
- eq(TEST_LETTERBOX_APPEARANCE.appearanceRegions),
- anyBoolean(),
- anyInt(),
- anyBoolean())
- }
-
- @Test
- fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToLightBarController() {
- changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
- reset(centralSurfaces, lightBarController)
-
- sysBarAttrsListener.onStatusBarBoundsChanged()
-
- verify(lightBarController)
- .onStatusBarAppearanceChanged(
- eq(TEST_LETTERBOX_APPEARANCE.appearanceRegions),
- anyBoolean(),
- anyInt(),
- anyBoolean())
- }
-
- @Test
- fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToCentralSurfaces() {
- changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
- reset(centralSurfaces, lightBarController)
-
- sysBarAttrsListener.onStatusBarBoundsChanged()
-
- verify(centralSurfaces).setAppearance(TEST_LETTERBOX_APPEARANCE.appearance)
- }
-
- @Test
- fun onStatusBarBoundsChanged_previousCallEmptyLetterbox_doesNothing() {
- changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf())
- reset(centralSurfaces, lightBarController)
-
- sysBarAttrsListener.onStatusBarBoundsChanged()
-
- verifyZeroInteractions(centralSurfaces, lightBarController)
- }
-
- private fun changeSysBarAttrs(@Appearance appearance: Int) {
- changeSysBarAttrs(appearance, arrayOf<LetterboxDetails>())
- }
-
- private fun changeSysBarAttrs(
- @Appearance appearance: Int,
- letterboxDetails: Array<LetterboxDetails>
- ) {
- changeSysBarAttrs(appearance, arrayOf(), letterboxDetails)
- }
-
- private fun changeSysBarAttrs(
- @Appearance appearance: Int,
- appearanceRegions: Array<AppearanceRegion>
- ) {
- changeSysBarAttrs(appearance, appearanceRegions, arrayOf())
- }
-
- private fun changeSysBarAttrs(
- @Appearance appearance: Int,
- appearanceRegions: Array<AppearanceRegion>,
- letterboxDetails: Array<LetterboxDetails>
- ) {
- sysBarAttrsListener.onSystemBarAttributesChanged(
- Display.DEFAULT_DISPLAY,
- appearance,
- appearanceRegions,
- /* navbarColorManagedByIme= */ false,
- WindowInsetsController.BEHAVIOR_DEFAULT,
- WindowInsets.Type.defaultVisible(),
- "package name",
- letterboxDetails)
- }
-
- companion object {
- private const val TEST_APPEARANCE =
- APPEARANCE_LIGHT_STATUS_BARS or APPEARANCE_LIGHT_NAVIGATION_BARS
- private val TEST_APPEARANCE_REGION = AppearanceRegion(TEST_APPEARANCE, Rect(0, 0, 150, 300))
- private val TEST_APPEARANCE_REGIONS = arrayOf(TEST_APPEARANCE_REGION)
- private val TEST_LETTERBOX_DETAILS =
- arrayOf(
- LetterboxDetails(
- /* letterboxInnerBounds= */ Rect(0, 0, 0, 0),
- /* letterboxFullBounds= */ Rect(0, 0, 0, 0),
- /* appAppearance= */ 0))
- private val TEST_LETTERBOX_APPEARANCE =
- LetterboxAppearance(/* appearance= */ APPEARANCE_LOW_PROFILE_BARS, arrayOf())
- }
-}
-
-private fun <T> anyObject(): T {
- return Mockito.anyObject<T>()
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 32320b4..49de512 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -40,6 +40,7 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
+import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
@@ -85,6 +86,7 @@
private val uiEventLoggerFake = UiEventLoggerFake()
private val testScope = TestScope()
private val statusBarModeRepository = FakeStatusBarModeRepository()
+ private val ongoingCallRepository = OngoingCallRepository()
private lateinit var controller: OngoingCallController
private lateinit var notifCollectionListener: NotifCollectionListener
@@ -111,6 +113,7 @@
controller = OngoingCallController(
testScope.backgroundScope,
context,
+ ongoingCallRepository,
notificationCollection,
clock,
mockActivityStarter,
@@ -140,10 +143,11 @@
}
@Test
- fun onEntryUpdated_isOngoingCallNotif_listenerNotified() {
+ fun onEntryUpdated_isOngoingCallNotif_listenerAndRepoNotified() {
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
+ assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
}
@Test
@@ -154,10 +158,11 @@
}
@Test
- fun onEntryUpdated_notOngoingCallNotif_listenerNotNotified() {
+ fun onEntryUpdated_notOngoingCallNotif_listenerAndRepoNotNotified() {
notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
verify(mockOngoingCallListener, never()).onOngoingCallStateChanged(anyBoolean())
+ assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
}
@Test
@@ -169,6 +174,16 @@
.onOngoingCallStateChanged(anyBoolean())
}
+ @Test
+ fun onEntryUpdated_ongoingCallNotifThenScreeningCallNotif_repoUpdated() {
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+ assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+
+ notifCollectionListener.onEntryUpdated(createScreeningCallNotifEntry())
+
+ assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+ }
+
/** Regression test for b/191472854. */
@Test
fun onEntryUpdated_notifHasNullContentIntent_noCrash() {
@@ -276,6 +291,17 @@
}
@Test
+ fun onEntryRemoved_callNotifAddedThenRemoved_repoUpdated() {
+ val ongoingCallNotifEntry = createOngoingCallNotifEntry()
+ notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
+ assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+
+ notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
+
+ assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+ }
+
+ @Test
fun onEntryUpdated_callNotifAddedThenRemoved_windowControllerUpdated() {
val ongoingCallNotifEntry = createOngoingCallNotifEntry()
notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
@@ -302,6 +328,22 @@
verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
}
+ /** Regression test for b/188491504. */
+ @Test
+ fun onEntryRemoved_removedNotifHasSameKeyAsAddedNotif_repoUpdated() {
+ val ongoingCallNotifEntry = createOngoingCallNotifEntry()
+ notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
+
+ // Create another notification based on the ongoing call one, but remove the features that
+ // made it a call notification.
+ val removedEntryBuilder = NotificationEntryBuilder(ongoingCallNotifEntry)
+ removedEntryBuilder.modifyNotification(context).style = null
+
+ notifCollectionListener.onEntryRemoved(removedEntryBuilder.build(), REASON_USER_STOPPED)
+
+ assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+ }
+
@Test
fun onEntryRemoved_notifKeyDoesNotMatchOngoingCallNotif_listenerNotNotified() {
notifCollectionListener.onEntryAdded(createOngoingCallNotifEntry())
@@ -313,6 +355,15 @@
}
@Test
+ fun onEntryRemoved_notifKeyDoesNotMatchOngoingCallNotif_repoNotUpdated() {
+ notifCollectionListener.onEntryAdded(createOngoingCallNotifEntry())
+
+ notifCollectionListener.onEntryRemoved(createNotCallNotifEntry(), REASON_USER_STOPPED)
+
+ assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+ }
+
+ @Test
fun hasOngoingCall_noOngoingCallNotifSent_returnsFalse() {
assertThat(controller.hasOngoingCall()).isFalse()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
new file mode 100644
index 0000000..56aa7d6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall.data.repository
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+@SmallTest
+class OngoingCallRepositoryTest : SysuiTestCase() {
+ private val underTest = OngoingCallRepository()
+
+ @Test
+ fun hasOngoingCall_matchesSet() {
+ underTest.setHasOngoingCall(true)
+
+ assertThat(underTest.hasOngoingCall.value).isTrue()
+
+ underTest.setHasOngoingCall(false)
+
+ assertThat(underTest.hasOngoingCall.value).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
index 15b9d61..c935dbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
@@ -17,13 +17,14 @@
package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Text
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.WifiIcons
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
@@ -181,6 +182,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(WifiIcons.WIFI_NO_INTERNET_ICONS[4]))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .doesNotContain(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -192,6 +195,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_tablet))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -203,6 +208,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_laptop))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -214,6 +221,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_watch))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -225,6 +234,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_auto))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -236,6 +247,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_phone))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -247,6 +260,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_phone))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -258,6 +273,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_phone))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index a520f6c..49a2648 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -18,8 +18,10 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.connectivity.WifiIcons
@@ -136,6 +138,10 @@
// is used instead
assertThat(latest).isInstanceOf(WifiIcon.Visible::class.java)
assertThat((latest as WifiIcon.Visible).res).isEqualTo(WifiIcons.WIFI_FULL_ICONS[1])
+ assertThat(
+ (latest as WifiIcon.Visible).contentDescription.loadContentDescription(context)
+ )
+ .doesNotContain(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index 85359ca..bfc5bdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -342,6 +342,31 @@
}
@Test
+ public void testShowToast_afterShowToast_animationListenerCleanup() throws RemoteException {
+ mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+ mCallback, Display.DEFAULT_DISPLAY);
+ final SystemUIToast toast = mToastUI.mToast;
+
+ View view = verifyWmAddViewAndAttachToParent();
+ mToastUI.showToast(UID_2, PACKAGE_NAME_2, TOKEN_2, TEXT, WINDOW_TOKEN_2, Toast.LENGTH_LONG,
+ null, Display.DEFAULT_DISPLAY);
+
+ if (toast.getOutAnimation() != null) {
+ assertThat(mToastUI.mToastOutAnimatorListener).isNotNull();
+ assertThat(toast.getOutAnimation().getListeners()
+ .contains(mToastUI.mToastOutAnimatorListener)).isTrue();
+ assertThat(toast.getOutAnimation().isRunning()).isTrue();
+ toast.getOutAnimation().cancel(); // end early if applicable
+ assertThat(toast.getOutAnimation().getListeners()).isNull();
+ }
+
+ verify(mWindowManager).removeViewImmediate(view);
+ verify(mNotificationManager).finishToken(PACKAGE_NAME_1, TOKEN_1);
+ verify(mCallback).onToastHidden();
+ assertThat(mToastUI.mToastOutAnimatorListener).isNull();
+ }
+
+ @Test
public void testShowToast_logs() {
mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
mCallback, Display.DEFAULT_DISPLAY);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 8e57dc1..daf8877 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -41,8 +41,8 @@
import android.app.KeyguardManager;
import android.content.res.Configuration;
import android.media.AudioManager;
-import android.os.Handler;
import android.os.SystemClock;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Log;
@@ -71,6 +71,7 @@
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.FakeConfigurationController;
+import com.android.systemui.util.settings.FakeSettings;
import org.junit.After;
import org.junit.Before;
@@ -135,6 +136,8 @@
private FakeFeatureFlags mFeatureFlags;
private int mLongestHideShowAnimationDuration = 250;
+ private FakeSettings mSecureSettings;
+
@Rule
public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
@@ -147,10 +150,6 @@
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
- // Ensure previous tests have not left messages on main looper
- Handler localHandler = new Handler(mTestableLooper.getLooper());
- localHandler.removeCallbacksAndMessages(null);
-
when(mPostureController.getDevicePosture())
.thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
@@ -167,6 +166,8 @@
mFeatureFlags = new FakeFeatureFlags();
+ mSecureSettings = new FakeSettings();
+
mDialog = new VolumeDialogImpl(
getContext(),
mVolumeDialogController,
@@ -182,7 +183,8 @@
mPostureController,
mTestableLooper.getLooper(),
mDumpManager,
- mFeatureFlags);
+ mFeatureFlags,
+ mSecureSettings);
mDialog.init(0, null);
State state = createShellState();
mDialog.onStateChangedH(state);
@@ -247,6 +249,18 @@
}
@Test
+ public void testSetTimeoutValue_ComputeTimeout() {
+ mSecureSettings.putInt(Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, 7000);
+ Mockito.reset(mAccessibilityMgr);
+ mDialog.init(0, null);
+ mDialog.rescheduleTimeoutH();
+ verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
+ 7000,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ }
+
+
+ @Test
public void testComputeTimeout_tooltip() {
Mockito.reset(mAccessibilityMgr);
mDialog.showCaptionsTooltip();
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 65b8b55..424218c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -87,16 +87,19 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.launcher3.icons.BubbleIconFactory;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.scene.FakeWindowRootViewComponent;
+import com.android.systemui.scene.SceneTestUtils;
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
@@ -104,11 +107,14 @@
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.ShadeWindowLogger;
+import com.android.systemui.shade.data.repository.FakeShadeRepository;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.NotificationEntryHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -121,15 +127,19 @@
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.user.domain.interactor.UserInteractor;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.Bubble;
@@ -176,6 +186,8 @@
import java.util.List;
import java.util.Optional;
+import kotlinx.coroutines.test.TestScope;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -299,6 +311,9 @@
@Mock
private Icon mAppBubbleIcon;
+ private SceneTestUtils mUtils = new SceneTestUtils(this);
+ private TestScope mTestScope = mUtils.getTestScope();
+ private ShadeInteractor mShadeInteractor;
private ShellTaskOrganizer mShellTaskOrganizer;
private TaskViewTransitions mTaskViewTransitions;
@@ -335,6 +350,22 @@
when(mNotificationShadeWindowView.getViewTreeObserver())
.thenReturn(mock(ViewTreeObserver.class));
+ mShadeInteractor = new ShadeInteractor(
+ mTestScope.getBackgroundScope(),
+ new FakeDisableFlagsRepository(),
+ new FakeSceneContainerFlags(),
+ mUtils::sceneInteractor,
+ new FakeKeyguardRepository(),
+ new FakeUserSetupRepository(),
+ mock(DeviceProvisionedController.class),
+ mock(UserInteractor.class),
+ new SharedNotificationContainerInteractor(
+ new FakeConfigurationRepository(),
+ mContext,
+ new ResourcesSplitShadeStateController()),
+ new FakeShadeRepository()
+ );
+
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
mContext,
new FakeWindowRootViewComponent.Factory(mNotificationShadeWindowView),
@@ -352,7 +383,9 @@
mScreenOffAnimationController,
mAuthController,
mShadeExpansionStateManager,
- mShadeWindowLogger);
+ () -> mShadeInteractor,
+ mShadeWindowLogger
+ );
mNotificationShadeWindowController.fetchWindowRootView();
mNotificationShadeWindowController.attach();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index f2e4528..4fc3e3f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -24,20 +24,19 @@
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationResultModel
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
class FakeAuthenticationRepository(
+ private val deviceEntryRepository: FakeDeviceEntryRepository,
private val currentTime: () -> Long,
) : AuthenticationRepository {
private val _isAutoConfirmEnabled = MutableStateFlow(false)
override val isAutoConfirmEnabled: StateFlow<Boolean> = _isAutoConfirmEnabled.asStateFlow()
- private val _isUnlocked = MutableStateFlow(false)
- override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow()
-
override val hintedPinLength: Int = HINTING_PIN_LENGTH
private val _isPatternVisible = MutableStateFlow(true)
@@ -53,7 +52,6 @@
override val minPatternLength: Int = 4
- private var isLockscreenEnabled = true
private var failedAttemptCount = 0
private var throttlingEndTimestamp = 0L
private var credentialOverride: List<Any>? = null
@@ -72,13 +70,9 @@
credentialOverride = pin
}
- override suspend fun isLockscreenEnabled(): Boolean {
- return isLockscreenEnabled
- }
-
override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) {
failedAttemptCount = if (isSuccessful) 0 else failedAttemptCount + 1
- _isUnlocked.value = isSuccessful
+ deviceEntryRepository.setUnlocked(isSuccessful)
}
override suspend fun getPinLength(): Int {
@@ -97,18 +91,10 @@
_throttling.value = throttlingModel
}
- fun setUnlocked(isUnlocked: Boolean) {
- _isUnlocked.value = isUnlocked
- }
-
fun setAutoConfirmEnabled(isEnabled: Boolean) {
_isAutoConfirmEnabled.value = isEnabled
}
- fun setLockscreenEnabled(isLockscreenEnabled: Boolean) {
- this.isLockscreenEnabled = isLockscreenEnabled
- }
-
override suspend fun setThrottleDuration(durationMs: Int) {
throttlingEndTimestamp = if (durationMs > 0) currentTime() + durationMs else 0
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
new file mode 100644
index 0000000..5e60a09
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
@@ -0,0 +1,35 @@
+package com.android.systemui.deviceentry.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Fake implementation of [DeviceEntryRepository] */
+class FakeDeviceEntryRepository : DeviceEntryRepository {
+
+ private var isInsecureLockscreenEnabled = true
+ private var isBypassEnabled = false
+
+ private val _isUnlocked = MutableStateFlow(false)
+ override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow()
+
+ override fun isBypassEnabled(): Boolean {
+ return isBypassEnabled
+ }
+
+ override suspend fun isInsecureLockscreenEnabled(): Boolean {
+ return isInsecureLockscreenEnabled
+ }
+
+ fun setUnlocked(isUnlocked: Boolean) {
+ _isUnlocked.value = isUnlocked
+ }
+
+ fun setInsecureLockscreenEnabled(isLockscreenEnabled: Boolean) {
+ this.isInsecureLockscreenEnabled = isLockscreenEnabled
+ }
+
+ fun setBypassEnabled(isBypassEnabled: Boolean) {
+ this.isBypassEnabled = isBypassEnabled
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index a5f5d52..aa52609 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -63,9 +63,6 @@
private val _isKeyguardShowing = MutableStateFlow(false)
override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
- private val _isKeyguardUnlocked = MutableStateFlow(false)
- override val isKeyguardUnlocked: StateFlow<Boolean> = _isKeyguardUnlocked.asStateFlow()
-
private val _isKeyguardOccluded = MutableStateFlow(false)
override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded
@@ -150,11 +147,6 @@
return _isKeyguardShowing.value
}
- private var _isBypassEnabled = false
- override fun isBypassEnabled(): Boolean {
- return _isBypassEnabled
- }
-
override fun setAnimateDozingTransitions(animate: Boolean) {
_animateBottomAreaDozingTransitions.tryEmit(animate)
}
@@ -252,14 +244,6 @@
_statusBarState.value = state
}
- fun setKeyguardUnlocked(isUnlocked: Boolean) {
- _isKeyguardUnlocked.value = isUnlocked
- }
-
- fun setBypassEnabled(isEnabled: Boolean) {
- _isBypassEnabled = isEnabled
- }
-
fun setScreenModel(screenModel: ScreenModel) {
_screenModel.value = screenModel
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
index 2e3bb2b..1cae09b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
@@ -19,6 +19,7 @@
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
@@ -42,6 +43,7 @@
sceneContainerFlags: SceneContainerFlags = FakeSceneContainerFlags(),
repository: FakeKeyguardRepository = FakeKeyguardRepository(),
commandQueue: FakeCommandQueue = FakeCommandQueue(),
+ deviceEntryRepository: FakeDeviceEntryRepository = FakeDeviceEntryRepository(),
bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
configurationRepository: FakeConfigurationRepository = FakeConfigurationRepository(),
shadeRepository: FakeShadeRepository = FakeShadeRepository(),
@@ -52,6 +54,7 @@
commandQueue = commandQueue,
featureFlags = featureFlags,
sceneContainerFlags = sceneContainerFlags,
+ deviceEntryRepository = deviceEntryRepository,
bouncerRepository = bouncerRepository,
configurationRepository = configurationRepository,
shadeRepository = shadeRepository,
@@ -60,6 +63,7 @@
commandQueue = commandQueue,
featureFlags = featureFlags,
sceneContainerFlags = sceneContainerFlags,
+ deviceEntryRepository = deviceEntryRepository,
bouncerRepository = bouncerRepository,
configurationRepository = configurationRepository,
shadeRepository = shadeRepository,
@@ -69,7 +73,7 @@
}
/** Provide defaults, otherwise tests will throw an error */
- fun createFakeFeatureFlags(): FakeFeatureFlags {
+ private fun createFakeFeatureFlags(): FakeFeatureFlags {
return FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
}
@@ -78,6 +82,7 @@
val commandQueue: FakeCommandQueue,
val featureFlags: FakeFeatureFlags,
val sceneContainerFlags: SceneContainerFlags,
+ val deviceEntryRepository: FakeDeviceEntryRepository,
val bouncerRepository: FakeKeyguardBouncerRepository,
val configurationRepository: FakeConfigurationRepository,
val shadeRepository: FakeShadeRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
index bf26e71..cbf4ae5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
@@ -21,6 +21,6 @@
class FakeQSFactory(private val tileCreator: (String) -> QSTile?) : QSFactory {
override fun createTile(tileSpec: String): QSTile? {
- return tileCreator(tileSpec)
+ return tileCreator(tileSpec)?.also { it.tileSpec = tileSpec }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
new file mode 100644
index 0000000..f62bf60
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.base.interactor
+
+class FakeDisabledByPolicyInteractor : DisabledByPolicyInteractor {
+
+ var handleResult: Boolean = false
+ var policyResult: DisabledByPolicyInteractor.PolicyResult =
+ DisabledByPolicyInteractor.PolicyResult.TileEnabled
+
+ override suspend fun isDisabled(
+ userId: Int,
+ userRestriction: String?
+ ): DisabledByPolicyInteractor.PolicyResult = policyResult
+
+ override fun handlePolicyResult(
+ policyResult: DisabledByPolicyInteractor.PolicyResult
+ ): Boolean = handleResult
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
index 13437c9..1cb4ab7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.qs.tiles.base.interactor
import javax.annotation.CheckReturnValue
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
index 4e0266e..9c99cb5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.qs.tiles.base.interactor
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 69c89e8..179206f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -34,6 +34,9 @@
import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
@@ -73,16 +76,10 @@
val testScope = TestScope(testDispatcher)
val featureFlags = FakeFeatureFlagsClassic().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
val sceneContainerFlags = FakeSceneContainerFlags().apply { enabled = true }
- private val userRepository: UserRepository by lazy {
- FakeUserRepository().apply {
- val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0))
- setUserInfos(users)
- runBlocking { setSelectedUserInfo(users.first()) }
- }
- }
-
+ val deviceEntryRepository: FakeDeviceEntryRepository by lazy { FakeDeviceEntryRepository() }
val authenticationRepository: FakeAuthenticationRepository by lazy {
FakeAuthenticationRepository(
+ deviceEntryRepository = deviceEntryRepository,
currentTime = { testScope.currentTime },
)
}
@@ -103,6 +100,14 @@
}
val powerRepository: FakePowerRepository by lazy { FakePowerRepository() }
+ private val userRepository: UserRepository by lazy {
+ FakeUserRepository().apply {
+ val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0))
+ setUserInfos(users)
+ runBlocking { setSelectedUserInfo(users.first()) }
+ }
+ }
+
private val context = test.context
private val falsingCollectorFake: FalsingCollector by lazy { FalsingCollectorFake() }
@@ -145,31 +150,41 @@
)
}
- fun authenticationRepository(): FakeAuthenticationRepository {
- return authenticationRepository
+ fun deviceEntryInteractor(
+ repository: DeviceEntryRepository = deviceEntryRepository,
+ authenticationInteractor: AuthenticationInteractor,
+ sceneInteractor: SceneInteractor,
+ ): DeviceEntryInteractor {
+ return DeviceEntryInteractor(
+ applicationScope = applicationScope(),
+ repository = repository,
+ authenticationInteractor = authenticationInteractor,
+ sceneInteractor = sceneInteractor,
+ )
}
fun authenticationInteractor(
- repository: AuthenticationRepository,
- sceneInteractor: SceneInteractor = sceneInteractor(),
+ repository: AuthenticationRepository = authenticationRepository,
): AuthenticationInteractor {
return AuthenticationInteractor(
applicationScope = applicationScope(),
repository = repository,
backgroundDispatcher = testDispatcher,
userRepository = userRepository,
- keyguardRepository = keyguardRepository,
- sceneInteractor = sceneInteractor,
+ deviceEntryRepository = deviceEntryRepository,
clock = mock { whenever(elapsedRealtime()).thenAnswer { testScope.currentTime } }
)
}
- fun keyguardInteractor(repository: KeyguardRepository): KeyguardInteractor {
+ fun keyguardInteractor(
+ repository: KeyguardRepository = keyguardRepository
+ ): KeyguardInteractor {
return KeyguardInteractor(
repository = repository,
commandQueue = FakeCommandQueue(),
featureFlags = featureFlags,
sceneContainerFlags = sceneContainerFlags,
+ deviceEntryRepository = FakeDeviceEntryRepository(),
bouncerRepository = FakeKeyguardBouncerRepository(),
configurationRepository = FakeConfigurationRepository(),
shadeRepository = FakeShadeRepository(),
@@ -185,6 +200,7 @@
}
fun bouncerInteractor(
+ deviceEntryInteractor: DeviceEntryInteractor,
authenticationInteractor: AuthenticationInteractor,
sceneInteractor: SceneInteractor,
): BouncerInteractor {
@@ -192,6 +208,7 @@
applicationScope = applicationScope(),
applicationContext = context,
repository = BouncerRepository(),
+ deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
flags = sceneContainerFlags,
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index 25f57b3..fb8db21 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -19,7 +19,10 @@
java_library_static {
name: "services.companion",
defaults: ["platform_service_defaults"],
- srcs: [":services.companion-sources"],
+ srcs: [
+ ":services.companion-sources",
+ ":VirtualCamera-aidl-sources",
+ ],
libs: [
"app-compat-annotations",
"services.core",
@@ -29,3 +32,11 @@
"virtualdevice_flags_lib",
],
}
+
+filegroup {
+ name: "VirtualCamera-aidl-sources",
+ srcs: [
+ "java/com/android/server/companion/virtual/camera/*.aidl",
+ ],
+ path: "java",
+}
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 b56b47f..9dd0dca 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -121,7 +121,7 @@
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
- final ArraySet<Integer> mRunningUids = new ArraySet<>();
+ private final ArraySet<Integer> mRunningUids = new ArraySet<>();
@Nullable private final ActivityListener mActivityListener;
@Nullable private final PipBlockedCallback mPipBlockedCallback;
@Nullable private final IntentListenerCallback mIntentListenerCallback;
diff --git a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
index 253ef43..3583a78 100644
--- a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
+++ b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
@@ -39,7 +39,7 @@
"include-filter": "android.hardware.input.cts.tests"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
],
"file_patterns": ["Virtual[^/]*\\.java"]
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 a2e4d2c..8a2aa61 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -50,6 +50,7 @@
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
+import android.companion.virtual.camera.IVirtualCamera;
import android.companion.virtual.flags.Flags;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorEvent;
@@ -105,6 +106,7 @@
import com.android.server.LocalServices;
import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener;
import com.android.server.companion.virtual.audio.VirtualAudioController;
+import com.android.server.companion.virtual.camera.VirtualCameraController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -114,7 +116,6 @@
import java.util.Set;
import java.util.function.Consumer;
-
final class VirtualDeviceImpl extends IVirtualDevice.Stub
implements IBinder.DeathRecipient, RunningAppsChangedListener {
@@ -178,6 +179,8 @@
private final InputController mInputController;
private final SensorController mSensorController;
private final CameraAccessController mCameraAccessController;
+ @Nullable // Null if virtual camera flag is off.
+ private final VirtualCameraController mVirtualCameraController;
private VirtualAudioController mVirtualAudioController;
private final IBinder mAppToken;
private final VirtualDeviceParams mParams;
@@ -270,7 +273,8 @@
soundEffectListener,
runningAppsChangedCallback,
params,
- DisplayManagerGlobal.getInstance());
+ DisplayManagerGlobal.getInstance(),
+ Flags.virtualCamera() ? new VirtualCameraController(context) : null);
}
@VisibleForTesting
@@ -289,7 +293,8 @@
IVirtualDeviceSoundEffectListener soundEffectListener,
Consumer<ArraySet<Integer>> runningAppsChangedCallback,
VirtualDeviceParams params,
- DisplayManagerGlobal displayManager) {
+ DisplayManagerGlobal displayManager,
+ VirtualCameraController virtualCameraController) {
super(PermissionEnforcer.fromContext(context));
mVirtualDeviceLog = virtualDeviceLog;
mOwnerPackageName = attributionSource.getPackageName();
@@ -324,6 +329,7 @@
} else {
mPermissionDialogComponent = null;
}
+ mVirtualCameraController = virtualCameraController;
try {
token.linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -564,6 +570,9 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
+ if (mVirtualCameraController != null) {
+ mVirtualCameraController.close();
+ }
}
@Override
@@ -920,24 +929,37 @@
}
}
+ @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void registerVirtualCamera(@NonNull IVirtualCamera camera) {
+ super.registerVirtualCamera_enforcePermission();
+ if (mVirtualCameraController == null) {
+ return;
+ }
+ mVirtualCameraController.registerCamera(Objects.requireNonNull(camera));
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ String indent = " ";
fout.println(" VirtualDevice: ");
- fout.println(" mDeviceId: " + mDeviceId);
- fout.println(" mAssociationId: " + mAssociationInfo.getId());
- fout.println(" mOwnerPackageName: " + mOwnerPackageName);
- fout.println(" mParams: ");
- mParams.dump(fout, " ");
- fout.println(" mVirtualDisplayIds: ");
+ fout.println(indent + "mDeviceId: " + mDeviceId);
+ fout.println(indent + "mAssociationId: " + mAssociationInfo.getId());
+ fout.println(indent + "mOwnerPackageName: " + mOwnerPackageName);
+ fout.println(indent + "mParams: ");
+ mParams.dump(fout, indent + indent);
+ fout.println(indent + "mVirtualDisplayIds: ");
synchronized (mVirtualDeviceLock) {
fout.println(" mDevicePolicies: " + mDevicePolicies);
for (int i = 0; i < mVirtualDisplays.size(); i++) {
- fout.println(" " + mVirtualDisplays.keyAt(i));
+ fout.println(indent + " " + mVirtualDisplays.keyAt(i));
}
- fout.println(" mDefaultShowPointerIcon: " + mDefaultShowPointerIcon);
+ fout.println(indent + "mDefaultShowPointerIcon: " + mDefaultShowPointerIcon);
}
mInputController.dump(fout);
mSensorController.dump(fout);
+ if (mVirtualCameraController != null) {
+ mVirtualCameraController.dump(fout, indent);
+ }
}
@GuardedBy("mVirtualDeviceLock")
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/IVirtualCameraService.aidl b/services/companion/java/com/android/server/companion/virtual/camera/IVirtualCameraService.aidl
new file mode 100644
index 0000000..a4c1c42
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/camera/IVirtualCameraService.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual.camera;
+
+import android.companion.virtual.camera.IVirtualCamera;
+import android.companion.virtual.camera.VirtualCameraHalConfig;
+
+/**
+ * AIDL Interface to communicate with the VirtualCamera HAL
+ * @hide
+ */
+interface IVirtualCameraService {
+
+ /**
+ * Registers a new camera with the virtual camera hal.
+ * @return true if the camera was successfully registered
+ */
+ boolean registerCamera(in IVirtualCamera camera);
+
+ /**
+ * Unregisters the camera from the virtual camera hal. After this call the virtual camera won't
+ * be visible to the camera framework anymore.
+ */
+ void unregisterCamera(in IVirtualCamera camera);
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
new file mode 100644
index 0000000..031d949
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual.camera;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.companion.virtual.camera.IVirtualCamera;
+import android.companion.virtual.camera.VirtualCameraHalConfig;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Manages the registration and removal of virtual camera from the server side.
+ *
+ * <p>This classes delegate calls to the virtual camera service, so it is dependent on the service
+ * to be up and running
+ */
+public class VirtualCameraController implements IBinder.DeathRecipient, ServiceConnection {
+
+ private static class VirtualCameraInfo {
+
+ private final IVirtualCamera mVirtualCamera;
+ private boolean mIsRegistered;
+
+ VirtualCameraInfo(IVirtualCamera virtualCamera) {
+ mVirtualCamera = virtualCamera;
+ }
+ }
+
+ private static final String TAG = "VirtualCameraController";
+
+ private static final String VIRTUAL_CAMERA_SERVICE_PACKAGE = "com.android.virtualcamera";
+ private static final String VIRTUAL_CAMERA_SERVICE_CLASS = ".VirtualCameraService";
+ private final Context mContext;
+
+ @Nullable private IVirtualCameraService mVirtualCameraService = null;
+
+ @GuardedBy("mCameras")
+ private final Map<IVirtualCamera, VirtualCameraInfo> mCameras = new HashMap<>(1);
+
+ public VirtualCameraController(Context context) {
+ mContext = context;
+ connectVirtualCameraService();
+ }
+
+ private void connectVirtualCameraService() {
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ Intent intent = new Intent();
+ intent.setPackage(VIRTUAL_CAMERA_SERVICE_PACKAGE);
+ intent.setComponent(
+ ComponentName.createRelative(
+ VIRTUAL_CAMERA_SERVICE_PACKAGE, VIRTUAL_CAMERA_SERVICE_CLASS));
+ mContext.startServiceAsUser(intent, UserHandle.SYSTEM);
+ if (!mContext.bindServiceAsUser(
+ intent,
+ this,
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
+ UserHandle.SYSTEM)) {
+ mContext.unbindService(this);
+ Log.w(
+ TAG,
+ "connectVirtualCameraService: Failed to connect to the virtual camera "
+ + "service");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
+ private void forwardPendingRegistrations() {
+ IVirtualCameraService cameraService = mVirtualCameraService;
+ if (cameraService == null) {
+ return;
+ }
+ synchronized (mCameras) {
+ for (VirtualCameraInfo cameraInfo : mCameras.values()) {
+ if (cameraInfo.mIsRegistered) {
+ continue;
+ }
+ try {
+ cameraService.registerCamera(cameraInfo.mVirtualCamera);
+ cameraInfo.mIsRegistered = true;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove the virtual camera with the provided name
+ *
+ * @param camera The name of the camera to remove
+ */
+ public void unregisterCamera(@NonNull IVirtualCamera camera) {
+ IVirtualCameraService virtualCameraService = mVirtualCameraService;
+ if (virtualCameraService != null) {
+ try {
+ virtualCameraService.unregisterCamera(camera);
+ synchronized (mCameras) {
+ VirtualCameraInfo cameraInfo = mCameras.remove(camera);
+ cameraInfo.mIsRegistered = false;
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Register a new virtual camera with the provided characteristics.
+ *
+ * @param camera The {@link IVirtualCamera} producing the image to communicate with the client.
+ * @throws IllegalArgumentException if the characteristics could not be parsed.
+ */
+ public void registerCamera(@NonNull IVirtualCamera camera) {
+ IVirtualCameraService service = mVirtualCameraService;
+ VirtualCameraInfo virtualCameraInfo = new VirtualCameraInfo(camera);
+ synchronized (mCameras) {
+ mCameras.put(camera, virtualCameraInfo);
+ }
+ if (service != null) {
+ try {
+ if (service.registerCamera(camera)) {
+ virtualCameraInfo.mIsRegistered = true;
+ return;
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ // Service was not available or registration failed, save the registration for later
+ connectVirtualCameraService();
+ }
+
+ @Override
+ public void binderDied() {
+ Log.d(TAG, "binderDied");
+ mVirtualCameraService = null;
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ mVirtualCameraService = null;
+ Log.d(TAG, "onBindingDied() called with: name = [" + name + "]");
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ mVirtualCameraService = null;
+ Log.d(TAG, "onNullBinding() called with: name = [" + name + "]");
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.d(TAG, "onServiceConnected: " + name.toString());
+ mVirtualCameraService = IVirtualCameraService.Stub.asInterface(service);
+ try {
+ service.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ forwardPendingRegistrations();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.d(TAG, "onServiceDisconnected() called with: name = [" + name + "]");
+ mVirtualCameraService = null;
+ }
+
+ /** Release resources associated with this controller. */
+ public void close() {
+ if (mVirtualCameraService == null) {
+ return;
+ }
+ synchronized (mCameras) {
+ mCameras.forEach(
+ (name, cameraInfo) -> {
+ try {
+ mVirtualCameraService.unregisterCamera(name);
+ } catch (RemoteException e) {
+ Log.w(
+ TAG,
+ "close(): Camera failed to be removed on camera service.",
+ e);
+ }
+ });
+ }
+ mContext.unbindService(this);
+ }
+
+ /** Dumps information about this {@link VirtualCameraController} for debugging purposes. */
+ public void dump(PrintWriter fout, String indent) {
+ fout.println(indent + "VirtualCameraController:");
+ indent += indent;
+ fout.printf("%sService:%s\n", indent, mVirtualCameraService);
+ synchronized (mCameras) {
+ fout.printf("%sRegistered cameras:%d%n\n", indent, mCameras.size());
+ for (VirtualCameraInfo info : mCameras.values()) {
+ VirtualCameraHalConfig config = null;
+ try {
+ config = info.mVirtualCamera.getHalConfig();
+ } catch (RemoteException ex) {
+ Log.w(TAG, ex);
+ }
+ fout.printf(
+ "%s- %s isRegistered: %s, token: %s\n",
+ indent,
+ config == null ? "" : config.displayName,
+ info.mIsRegistered,
+ info.mVirtualCamera);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 2a46d86..a1e6b58f 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -81,7 +81,7 @@
* Returns whether an intent matches the IntentFilter with a pre-resolved type.
*/
public static boolean intentMatchesFilter(
- IntentFilter filter, Intent intent, String resolvedType) {
+ IntentFilter filter, Intent intent, String resolvedType, boolean defaultOnly) {
final boolean debug = localLOGV
|| ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
@@ -94,9 +94,13 @@
filter.dump(logPrinter, " ");
}
- final int match = filter.match(intent.getAction(), resolvedType, intent.getScheme(),
+ int match = filter.match(intent.getAction(), resolvedType, intent.getScheme(),
intent.getData(), intent.getCategories(), TAG);
+ if (match >= 0 && defaultOnly && !filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
+ match = IntentFilter.NO_MATCH_CATEGORY;
+ }
+
if (match >= 0) {
if (debug) {
Slog.v(TAG, "Filter matched! match=0x" + Integer.toHexString(match));
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index d6f1348..ced7773 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -342,7 +342,10 @@
// Check if user is associated with the subscription
if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId,
- Binder.getCallingUserHandle())) {
+ Binder.getCallingUserHandle())
+ // For inactive sub, fall through to MMS service to have it recorded in metrics.
+ && isActiveSubId(subId)) {
+ // Try remind user to use another profile to send.
TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext,
subId, Binder.getCallingUid(), callingPkg);
return;
@@ -550,6 +553,17 @@
}
}
+ /** @return true if the subId is active. */
+ private boolean isActiveSubId(int subId) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
+ return subManager != null && subManager.isActiveSubscriptionId(subId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private int getPhoneIdFromSubId(int subId) {
SubscriptionManager subManager = (SubscriptionManager)
mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 2c83c6f..6cca130 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -150,16 +150,7 @@
public boolean isChangeEnabledByUid(long changeId, int uid) {
super.isChangeEnabledByUid_enforcePermission();
- String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
- if (packages == null || packages.length == 0) {
- return mCompatConfig.defaultChangeIdValue(changeId);
- }
- boolean enabled = true;
- for (String packageName : packages) {
- enabled &= isChangeEnabledByPackageName(changeId, packageName,
- UserHandle.getUserId(uid));
- }
- return enabled;
+ return isChangeEnabledByUidInternal(changeId, uid);
}
/**
@@ -208,6 +199,25 @@
return false;
}
+ /**
+ * Internal version of {@link #isChangeEnabledByUid(long, int)}.
+ *
+ * <p>Does not perform costly permission check.
+ */
+ public boolean isChangeEnabledByUidInternal(long changeId, int uid) {
+ String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+ if (packages == null || packages.length == 0) {
+ return mCompatConfig.defaultChangeIdValue(changeId);
+ }
+ boolean enabled = true;
+ final int userId = UserHandle.getUserId(uid);
+ for (String packageName : packages) {
+ final var appInfo = getApplicationInfo(packageName, userId);
+ enabled &= isChangeEnabledInternal(changeId, appInfo);
+ }
+ return enabled;
+ }
+
@Override
@EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
diff --git a/services/core/java/com/android/server/connectivity/OWNERS b/services/core/java/com/android/server/connectivity/OWNERS
index 62c5737..c24680e9 100644
--- a/services/core/java/com/android/server/connectivity/OWNERS
+++ b/services/core/java/com/android/server/connectivity/OWNERS
@@ -1,2 +1,2 @@
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index debf828..99a5398 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -33,7 +33,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
-import com.android.internal.display.BrightnessUtils;
import com.android.internal.util.Preconditions;
import com.android.server.display.utils.Plog;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
diff --git a/core/java/com/android/internal/display/BrightnessUtils.java b/services/core/java/com/android/server/display/BrightnessUtils.java
similarity index 96%
rename from core/java/com/android/internal/display/BrightnessUtils.java
rename to services/core/java/com/android/server/display/BrightnessUtils.java
index 82b506b..84fa0cc 100644
--- a/core/java/com/android/internal/display/BrightnessUtils.java
+++ b/services/core/java/com/android/server/display/BrightnessUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.display;
+package com.android.server.display;
import android.util.MathUtils;
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index e38c2c5..5ba042c 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -20,8 +20,6 @@
import android.util.FloatProperty;
import android.view.Choreographer;
-import com.android.internal.display.BrightnessUtils;
-
/**
* A custom animator that progressively updates a property value at
* a given variable rate until it reaches a particular target value.
diff --git a/services/core/java/com/android/server/display/TEST_MAPPING b/services/core/java/com/android/server/display/TEST_MAPPING
index 6f243e1..049b2fd 100644
--- a/services/core/java/com/android/server/display/TEST_MAPPING
+++ b/services/core/java/com/android/server/display/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "DisplayServiceTests",
"options": [
{"include-filter": "com.android.server.display"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
index e46edd9..652e6cf 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
@@ -24,6 +24,7 @@
import android.view.SurfaceControlHdrLayerInfoListener;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.BrightnessUtils;
import com.android.server.display.config.HdrBrightnessData;
import java.io.PrintWriter;
@@ -178,8 +179,12 @@
debounceTime = mHdrBrightnessData.mBrightnessDecreaseDebounceMillis;
transitionDuration = mHdrBrightnessData.mBrightnessDecreaseDurationMillis;
}
+
+ float maxHlg = BrightnessUtils.convertLinearToGamma(mMaxBrightness);
+ float desiredMaxHlg = BrightnessUtils.convertLinearToGamma(mDesiredMaxBrightness);
+
mDesiredTransitionRate = Math.abs(
- (mMaxBrightness - mDesiredMaxBrightness) * 1000f / transitionDuration);
+ (maxHlg - desiredMaxHlg) * 1000f / transitionDuration);
mHandler.removeCallbacks(mDebouncer);
mHandler.postDelayed(mDebouncer, debounceTime);
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
index 41053e9..68848a2 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
@@ -16,36 +16,66 @@
package com.android.server.grammaticalinflection;
+import static android.app.Flags.systemTermsOfAddressEnabled;
import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
import android.annotation.Nullable;
+import android.app.GrammaticalInflectionManager;
import android.app.IGrammaticalInflectionManager;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
-import android.os.IBinder;
+import android.os.Environment;
import android.os.Process;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.SystemProperties;
+import android.util.AtomicFile;
import android.util.Log;
+import android.util.SparseIntArray;
+import android.util.Xml;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityTaskManagerInternal;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
/**
* The implementation of IGrammaticalInflectionManager.aidl.
*
* <p>This service is API entry point for storing app-specific grammatical inflection.
*/
public class GrammaticalInflectionService extends SystemService {
- private final String TAG = "GrammaticalInflection";
- private final GrammaticalInflectionBackupHelper mBackupHelper;
- private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
- private PackageManagerInternal mPackageManagerInternal;
+ private static final String TAG = "GrammaticalInflection";
+ private static final String ATTR_NAME = "grammatical_gender";
+ private static final String USER_SETTINGS_FILE_NAME = "user_settings.xml";
+ private static final String TAG_GRAMMATICAL_INFLECTION = "grammatical_inflection";
private static final String GRAMMATICAL_INFLECTION_ENABLED =
"i18n.grammatical_Inflection.enabled";
+ private final GrammaticalInflectionBackupHelper mBackupHelper;
+ private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+ private final Object mLock = new Object();
+ private final SparseIntArray mGrammaticalGenderCache = new SparseIntArray();
+
+ private PackageManagerInternal mPackageManagerInternal;
+ private GrammaticalInflectionService.GrammaticalInflectionBinderService mBinderService;
+
/**
* Initializes the system service.
* <p>
@@ -62,22 +92,46 @@
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mBackupHelper = new GrammaticalInflectionBackupHelper(
this, context.getPackageManager());
+ mBinderService = new GrammaticalInflectionBinderService();
}
@Override
public void onStart() {
- publishBinderService(Context.GRAMMATICAL_INFLECTION_SERVICE, mService);
+ publishBinderService(Context.GRAMMATICAL_INFLECTION_SERVICE, mBinderService);
LocalServices.addService(GrammaticalInflectionManagerInternal.class,
new GrammaticalInflectionManagerInternalImpl());
}
- private final IBinder mService = new IGrammaticalInflectionManager.Stub() {
+ private final class GrammaticalInflectionBinderService extends
+ IGrammaticalInflectionManager.Stub {
@Override
public void setRequestedApplicationGrammaticalGender(
String appPackageName, int userId, int gender) {
GrammaticalInflectionService.this.setRequestedApplicationGrammaticalGender(
appPackageName, userId, gender);
}
+
+ @Override
+ public void setSystemWideGrammaticalGender(int userId, int grammaticalGender) {
+ checkCallerIsSystem();
+ checkSystemTermsOfAddressIsEnabled();
+ GrammaticalInflectionService.this.setSystemWideGrammaticalGender(grammaticalGender,
+ userId);
+ }
+
+ @Override
+ public int getSystemGrammaticalGender(int userId) {
+ checkSystemTermsOfAddressIsEnabled();
+ return GrammaticalInflectionService.this.getSystemGrammaticalGender(userId);
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ (new GrammaticalInflectionShellCommand(mBinderService))
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
};
private final class GrammaticalInflectionManagerInternalImpl
@@ -94,12 +148,6 @@
public void stageAndApplyRestoredPayload(byte[] payload, int userId) {
mBackupHelper.stageAndApplyRestoredPayload(payload, userId);
}
-
- private void checkCallerIsSystem() {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Caller is not system.");
- }
- }
}
protected int getApplicationGrammaticalGender(String appPackageName, int userId) {
@@ -137,4 +185,105 @@
updater.setGrammaticalGender(gender).commit();
}
+
+ protected void setSystemWideGrammaticalGender(int grammaticalGender, int userId) {
+ if (!GrammaticalInflectionManager.VALID_GRAMMATICAL_GENDER_VALUES.contains(
+ grammaticalGender)) {
+ throw new IllegalArgumentException("Unknown grammatical gender");
+ }
+
+ synchronized (mLock) {
+ final File file = getGrammaticalGenderFile(userId);
+ final AtomicFile atomicFile = new AtomicFile(file);
+ FileOutputStream stream = null;
+ try {
+ stream = atomicFile.startWrite();
+ stream.write(toXmlByteArray(grammaticalGender, stream));
+ atomicFile.finishWrite(stream);
+ mGrammaticalGenderCache.put(userId, grammaticalGender);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write file " + atomicFile, e);
+ if (stream != null) {
+ atomicFile.failWrite(stream);
+ }
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ // TODO(b/298591009): Add a new AppOp value for the apps that want to access the grammatical
+ // gender.
+ public int getSystemGrammaticalGender(int userId) {
+ synchronized (mLock) {
+ final File file = getGrammaticalGenderFile(userId);
+ if (!file.exists()) {
+ Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file.");
+ return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ }
+
+ if (mGrammaticalGenderCache.indexOfKey(userId) < 0) {
+ try {
+ InputStream in = new FileInputStream(file);
+ final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+ mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser));
+ } catch (IOException | XmlPullParserException e) {
+ Log.e(TAG, "Failed to parse XML configuration from " + file, e);
+ }
+ }
+ return mGrammaticalGenderCache.get(userId);
+ }
+ }
+
+ private File getGrammaticalGenderFile(int userId) {
+ final File dir = new File(Environment.getDataSystemCeDirectory(userId),
+ TAG_GRAMMATICAL_INFLECTION);
+ return new File(dir, USER_SETTINGS_FILE_NAME);
+ }
+
+ private byte[] toXmlByteArray(int grammaticalGender, FileOutputStream fileStream) {
+
+ try {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ TypedXmlSerializer out = Xml.resolveSerializer(fileStream);
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ out.startDocument(/* encoding= */ null, /* standalone= */ true);
+ out.startTag(null, TAG_GRAMMATICAL_INFLECTION);
+ out.attributeInt(null, ATTR_NAME, grammaticalGender);
+ out.endTag(null, TAG_GRAMMATICAL_INFLECTION);
+ out.endDocument();
+
+ return outputStream.toByteArray();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private int getGrammaticalGenderFromXml(TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if (TAG_GRAMMATICAL_INFLECTION.equals(tagName)) {
+ return parser.getAttributeInt(null, ATTR_NAME);
+ } else {
+ XmlUtils.nextElement(parser);
+ }
+ }
+
+ return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ }
+
+ private void checkCallerIsSystem() {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID && callingUid != Process.SHELL_UID) {
+ throw new SecurityException("Caller is not system and shell.");
+ }
+ }
+
+ private void checkSystemTermsOfAddressIsEnabled() {
+ if (!systemTermsOfAddressEnabled()) {
+ throw new RuntimeException("The flag must be enabled to allow calling the API.");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionShellCommand.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionShellCommand.java
new file mode 100644
index 0000000..d223728
--- /dev/null
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionShellCommand.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.grammaticalinflection;
+
+import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
+
+import android.app.ActivityManager;
+import android.app.GrammaticalInflectionManager;
+import android.app.IGrammaticalInflectionManager;
+import android.content.res.Configuration;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell commands for {@link GrammaticalInflectionService}
+ */
+class GrammaticalInflectionShellCommand extends ShellCommand {
+
+ private static final SparseArray<String> GRAMMATICAL_GENDER_MAP = new SparseArray<>();
+ static {
+ GRAMMATICAL_GENDER_MAP.put(Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED,
+ "Not specified (0)");
+ GRAMMATICAL_GENDER_MAP.put(Configuration.GRAMMATICAL_GENDER_NEUTRAL, "Neuter (1)");
+ GRAMMATICAL_GENDER_MAP.put(Configuration.GRAMMATICAL_GENDER_FEMININE, "Feminine (2)");
+ GRAMMATICAL_GENDER_MAP.put(Configuration.GRAMMATICAL_GENDER_MASCULINE, "Masculine (3)");
+ }
+
+ private final IGrammaticalInflectionManager mBinderService;
+
+ GrammaticalInflectionShellCommand(IGrammaticalInflectionManager grammaticalInflectionManager) {
+ mBinderService = grammaticalInflectionManager;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ switch (cmd) {
+ case "set-system-grammatical-gender":
+ return runSetSystemWideGrammaticalGender();
+ case "get-system-grammatical-gender":
+ return runGetSystemGrammaticalGender();
+ default: {
+ return handleDefaultCommands(cmd);
+ }
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Grammatical inflection manager (grammatical_inflection) shell commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(
+ " set-system-grammatical-gender [--user <USER_ID>] [--grammaticalGender "
+ + "<GRAMMATICAL_GENDER>]");
+ pw.println(" Set the system grammatical gender for system.");
+ pw.println(" --user <USER_ID>: apply for the given user, "
+ + "the current user is used when unspecified.");
+ pw.println(
+ " --grammaticalGender <GRAMMATICAL_GENDER>: The terms of address the user "
+ + "preferred in system, not specified (0) is used when unspecified.");
+ pw.println(
+ " eg. 0 = not_specified, 1 = neuter, 2 = feminine, 3 = masculine"
+ + ".");
+ pw.println(
+ " get-system-grammatical-gender [--user <USER_ID>]");
+ pw.println(" Get the system grammatical gender for system.");
+ pw.println(" --user <USER_ID>: apply for the given user, "
+ + "the current user is used when unspecified.");
+ }
+
+ private int runSetSystemWideGrammaticalGender() {
+ int userId = ActivityManager.getCurrentUser();
+ int grammaticalGender = GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ do {
+ String option = getNextOption();
+ if (option == null) {
+ break;
+ }
+ switch (option) {
+ case "--user": {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ }
+ case "-g":
+ case "--grammaticalGender": {
+ grammaticalGender = parseGrammaticalGender();
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown option: " + option);
+ }
+ }
+ } while (true);
+
+ try {
+ mBinderService.setSystemWideGrammaticalGender(userId, grammaticalGender);
+ } catch (RemoteException e) {
+ getOutPrintWriter().println("Remote Exception: " + e);
+ }
+ return 0;
+ }
+
+ private int runGetSystemGrammaticalGender() {
+ int userId = ActivityManager.getCurrentUser();
+ do {
+ String option = getNextOption();
+ if (option == null) {
+ break;
+ }
+ switch (option) {
+ case "--user": {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown option: " + option);
+ }
+ }
+ } while (true);
+
+ try {
+ int grammaticalGender = mBinderService.getSystemGrammaticalGender(userId);
+ getOutPrintWriter().println(GRAMMATICAL_GENDER_MAP.get(grammaticalGender));
+ } catch (RemoteException e) {
+ getOutPrintWriter().println("Remote Exception: " + e);
+ }
+ return 0;
+ }
+
+ private int parseGrammaticalGender() {
+ String arg = getNextArg();
+ if (arg == null) {
+ return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ } else {
+ int grammaticalGender = Integer.parseInt(arg);
+ if (GrammaticalInflectionManager.VALID_GRAMMATICAL_GENDER_VALUES.contains(
+ grammaticalGender)) {
+ return grammaticalGender;
+ } else {
+ return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/grammaticalinflection/OWNERS b/services/core/java/com/android/server/grammaticalinflection/OWNERS
index 5f16ba9..41d079e 100644
--- a/services/core/java/com/android/server/grammaticalinflection/OWNERS
+++ b/services/core/java/com/android/server/grammaticalinflection/OWNERS
@@ -2,3 +2,4 @@
allenwtsu@google.com
goldmanj@google.com
calvinpan@google.com
+zoeychen@google.com
diff --git a/services/core/java/com/android/server/lights/TEST_MAPPING b/services/core/java/com/android/server/lights/TEST_MAPPING
index f868ea0..17b98ce8 100644
--- a/services/core/java/com/android/server/lights/TEST_MAPPING
+++ b/services/core/java/com/android/server/lights/TEST_MAPPING
@@ -4,16 +4,14 @@
"name": "CtsHardwareTestCases",
"options": [
{"include-filter": "com.android.hardware.lights"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"}
]
},
{
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.lights"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/services/core/java/com/android/server/locksettings/TEST_MAPPING b/services/core/java/com/android/server/locksettings/TEST_MAPPING
index b881b44..ddf3d76 100644
--- a/services/core/java/com/android/server/locksettings/TEST_MAPPING
+++ b/services/core/java/com/android/server/locksettings/TEST_MAPPING
@@ -7,7 +7,7 @@
"include-annotation": "com.android.cts.devicepolicy.annotations.LockSettingsTest"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
@@ -20,7 +20,7 @@
"include-filter": "com.android.server.locksettings."
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/services/core/java/com/android/server/logcat/TEST_MAPPING b/services/core/java/com/android/server/logcat/TEST_MAPPING
index f4b13a0..9041552 100644
--- a/services/core/java/com/android/server/logcat/TEST_MAPPING
+++ b/services/core/java/com/android/server/logcat/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.logcat"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/services/core/java/com/android/server/media/projection/TEST_MAPPING b/services/core/java/com/android/server/media/projection/TEST_MAPPING
index a792498..7aa9118 100644
--- a/services/core/java/com/android/server/media/projection/TEST_MAPPING
+++ b/services/core/java/com/android/server/media/projection/TEST_MAPPING
@@ -4,9 +4,6 @@
"name": "MediaProjectionTests",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 9c96d46f..d0e95dd 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -1,5 +1,5 @@
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
jsharkey@android.com
sudheersai@google.com
diff --git a/services/core/java/com/android/server/notification/TEST_MAPPING b/services/core/java/com/android/server/notification/TEST_MAPPING
index 7db2e8b..468c451 100644
--- a/services/core/java/com/android/server/notification/TEST_MAPPING
+++ b/services/core/java/com/android/server/notification/TEST_MAPPING
@@ -4,9 +4,6 @@
"name": "CtsNotificationTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
@@ -14,9 +11,6 @@
},
{
"exclude-annotation": "androidx.test.filters.LargeTest"
- },
- {
- "exclude-annotation": "androidx.test.filters.LargeTest"
}
]
},
@@ -24,9 +18,6 @@
"name": "FrameworksUiServicesTests",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
@@ -34,9 +25,6 @@
},
{
"exclude-annotation": "androidx.test.filters.LargeTest"
- },
- {
- "exclude-annotation": "androidx.test.filters.LargeTest"
}
]
}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 6baa889..5d2944e 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -580,7 +580,7 @@
list.add(ri);
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
mInjector.getCompatibility(), mComponentResolver,
- list, false, intent, resolvedType, filterCallingUid);
+ list, false, intent, resolvedType, flags, filterCallingUid);
}
}
} else {
@@ -610,7 +610,7 @@
// We also have to ensure all components match the original intent
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
mInjector.getCompatibility(), mComponentResolver,
- list, false, originalIntent, resolvedType, filterCallingUid);
+ list, false, originalIntent, resolvedType, flags, filterCallingUid);
}
return skipPostResolution ? list : applyPostResolutionFilter(
@@ -699,7 +699,7 @@
list.add(ri);
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
mInjector.getCompatibility(), mComponentResolver,
- list, false, intent, resolvedType, callingUid);
+ list, false, intent, resolvedType, flags, callingUid);
}
}
} else {
@@ -711,7 +711,7 @@
// We also have to ensure all components match the original intent
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
mInjector.getCompatibility(), mComponentResolver,
- list, false, originalIntent, resolvedType, callingUid);
+ list, false, originalIntent, resolvedType, flags, callingUid);
}
return list;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 38f241d..8e91f42 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -44,6 +44,7 @@
import android.app.ActivityManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
+import android.compat.annotation.Overridable;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -69,6 +70,7 @@
import android.os.FileUtils;
import android.os.Process;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
import android.os.incremental.V4Signature;
@@ -180,8 +182,9 @@
public @interface SharedUserIdJoinType {}
/**
- * Components of apps targeting Android T and above will stop receiving intents from
- * external callers that do not match its declared intent filters.
+ * Intents sent from apps targeting Android V and above will stop resolving to components with
+ * non matching intent filters, even when explicitly setting a component name, unless the
+ * target components are in the same app as the calling app.
*
* When an app registers an exported component in its manifest and adds an <intent-filter>,
* the component can be started by any intent - even those that do not match the intent filter.
@@ -189,8 +192,9 @@
* Without checking the intent when the component is started, in some circumstances this can
* allow 3P apps to trigger internal-only functionality.
*/
+ @Overridable
@ChangeId
- @Disabled /* Revert enforcement: b/274147456 */
+ @Disabled /* Enforcement reverted in T: b/274147456 */
private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188;
/**
@@ -1187,19 +1191,27 @@
public static void applyEnforceIntentFilterMatching(
PlatformCompat compat, ComponentResolverApi resolver,
List<ResolveInfo> resolveInfos, boolean isReceiver,
- Intent intent, String resolvedType, int filterCallingUid) {
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
+ int filterCallingUid) {
if (DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.get()) return;
+ // Do not enforce filter matching when the caller is system or root
+ if (ActivityManager.canAccessUnexportedComponents(filterCallingUid)) return;
+
final Printer logPrinter = DEBUG_INTENT_MATCHING
? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM)
: null;
+ final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
+
+ final boolean enforce = compat.isChangeEnabledByUidInternal(
+ ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, filterCallingUid);
+
for (int i = resolveInfos.size() - 1; i >= 0; --i) {
final ComponentInfo info = resolveInfos.get(i).getComponentInfo();
- // Do not enforce filter matching when the caller is system, root, or the same app
- if (ActivityManager.checkComponentPermission(null, filterCallingUid,
- info.applicationInfo.uid, false) == PackageManager.PERMISSION_GRANTED) {
+ // Skip filter matching when the caller is targeting the same app
+ if (UserHandle.isSameApp(filterCallingUid, info.applicationInfo.uid)) {
continue;
}
@@ -1221,14 +1233,11 @@
continue;
}
- // Only enforce filter matching if target app's target SDK >= T
- final boolean enforce = compat.isChangeEnabledInternal(
- ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, info.applicationInfo);
-
boolean match = false;
for (int j = 0, size = comp.getIntents().size(); j < size; ++j) {
IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter();
- if (IntentResolver.intentMatchesFilter(intentFilter, intent, resolvedType)) {
+ if (IntentResolver.intentMatchesFilter(
+ intentFilter, intent, resolvedType, defaultOnly)) {
match = true;
break;
}
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index 160b2aa..da14397 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -459,7 +459,7 @@
list.add(ri);
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
mPlatformCompat, componentResolver, list, true, intent,
- resolvedType, filterCallingUid);
+ resolvedType, flags, filterCallingUid);
}
}
} else {
@@ -485,7 +485,7 @@
// We also have to ensure all components match the original intent
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
mPlatformCompat, componentResolver,
- list, true, originalIntent, resolvedType, filterCallingUid);
+ list, true, originalIntent, resolvedType, flags, filterCallingUid);
}
return computer.applyPostResolutionFilter(list, instantAppPkgName, false, queryingUid,
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 04d1da6..0fd7a37 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -55,6 +55,7 @@
},
{
"name": "GtsContentTestCases",
+ "keywords": ["internal"],
"options": [
{
"include-filter": "com.google.android.content.gts"
@@ -145,6 +146,19 @@
"include-filter": "android.content.pm.cts.PackageManagerShellCommandMultiUserTest"
}
]
+ },
+ {
+ "file_patterns": [
+ "(/|^)InstallPackageHelper\\.java",
+ "services/core/java/com/android/server/pm/parsing/.*",
+ "services/core/java/com/android/server/pm/pkg/parsing/.*"
+ ],
+ "name": "SdkSandboxManagerServiceUnitTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
],
"imports": [
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index dfc9b8b..097656c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -198,7 +198,6 @@
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
-import com.android.internal.display.BrightnessUtils;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
@@ -218,6 +217,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
import com.android.server.UiThread;
+import com.android.server.display.BrightnessUtils;
import com.android.server.input.InputManagerInternal;
import com.android.server.input.KeyboardMetricsCollector;
import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
index 819a82c..338b479 100644
--- a/services/core/java/com/android/server/policy/TEST_MAPPING
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -32,7 +32,7 @@
"name": "CtsPermissionPolicyTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permissionpolicy.cts.RestrictedPermissionsTest"
@@ -49,7 +49,7 @@
"name": "CtsPermissionTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permission.cts.SplitPermissionTest"
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4a4214f..dfbcbae6 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3347,6 +3347,8 @@
} else {
startDreaming = false;
}
+ Slog.i(TAG, "handleSandman powerGroup=" + groupId + " startDreaming=" + startDreaming
+ + " wakefulness=" + wakefulnessToString(wakefulness));
}
// Start dreaming if needed.
@@ -3381,19 +3383,23 @@
if (startDreaming && isDreaming) {
mDreamsBatteryLevelDrain = 0;
if (wakefulness == WAKEFULNESS_DOZING) {
- Slog.i(TAG, "Dozing...");
+ Slog.i(TAG, "Dozing powerGroup " + groupId);
} else {
- Slog.i(TAG, "Dreaming...");
+ Slog.i(TAG, "Dreaming powerGroup " + groupId);
}
}
// If preconditions changed, wait for the next iteration to determine
// whether the dream should continue (or be restarted).
final PowerGroup powerGroup = mPowerGroups.get(groupId);
+ final int newWakefulness = powerGroup.getWakefulnessLocked();
if (powerGroup.isSandmanSummonedLocked()
- || powerGroup.getWakefulnessLocked() != wakefulness) {
+ || newWakefulness != wakefulness) {
return; // wait for next cycle
}
+ Slog.i(TAG, "handleSandman powerGroup=" + groupId + " isDreaming=" + isDreaming
+ + " wakefulness=" + newWakefulness);
+
// Determine whether the dream should continue.
long now = mClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index 19086a1..05a0e85 100644
--- a/services/core/java/com/android/server/power/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -3,16 +3,14 @@
{
"name": "CtsBatterySavingTestCases",
"options": [
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"}
]
},
{
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.power"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -20,7 +18,6 @@
"name": "PowerServiceTests",
"options": [
{"include-filter": "com.android.server.power"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/services/core/java/com/android/server/security/rkp/OWNERS b/services/core/java/com/android/server/security/rkp/OWNERS
index 348f940..ea6dc72 100644
--- a/services/core/java/com/android/server/security/rkp/OWNERS
+++ b/services/core/java/com/android/server/security/rkp/OWNERS
@@ -1 +1 @@
-file:platform/frameworks/base:master:/core/java/android/security/rkp/OWNERS
+file:platform/frameworks/base:main:/core/java/android/security/rkp/OWNERS
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 88eaafa..3fd8323 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -2115,7 +2115,7 @@
IStatusBar bar = mBar;
if (bar != null) {
try {
- bar.requestAddTile(componentName, appName, label, icon, proxyCallback);
+ bar.requestAddTile(callingUid, componentName, appName, label, icon, proxyCallback);
return;
} catch (RemoteException e) {
Slog.e(TAG, "requestAddTile", e);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a0c7870..ed10346 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -580,7 +580,7 @@
IBinder mRequestedLaunchingTaskFragmentToken;
// Tracking splash screen status from previous activity
- boolean mAllowIconSplashScreen = true;
+ boolean mSplashScreenStyleSolidColor = false;
boolean mPauseSchedulePendingForPip = false;
@@ -2408,7 +2408,8 @@
@VisibleForTesting
boolean addStartingWindow(String pkg, int resolvedTheme, ActivityRecord from, boolean newTask,
boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot,
- boolean activityCreated, boolean allowIcon, boolean activityAllDrawn) {
+ boolean activityCreated, boolean isSimple,
+ boolean activityAllDrawn) {
// If the display is frozen, we won't do anything until the actual window is
// displayed so there is no reason to put in the starting window.
if (!okToDisplay()) {
@@ -2443,8 +2444,8 @@
final int typeParameter = StartingSurfaceController
.makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning,
- allowTaskSnapshot, activityCreated, allowIcon, useLegacy,
- activityAllDrawn, type, packageName, mUserId);
+ allowTaskSnapshot, activityCreated, isSimple, useLegacy, activityAllDrawn,
+ type, packageName, mUserId);
if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
if (isActivityTypeHome()) {
@@ -5365,7 +5366,7 @@
// Finish should only ever commit visibility=false, so we can check full containment
// rather than just direct membership.
inFinishingTransition = mTransitionController.inFinishingTransition(this);
- if (!inFinishingTransition && !mDisplayContent.isSleeping()) {
+ if (!inFinishingTransition && (visible || !mDisplayContent.isSleeping())) {
Slog.e(TAG, "setVisibility=" + visible
+ " while transition is not collecting or finishing "
+ this + " caller=" + Debug.getCallers(8));
@@ -6746,7 +6747,7 @@
void onFirstWindowDrawn(WindowState win) {
firstWindowDrawn = true;
// stop tracking
- mAllowIconSplashScreen = false;
+ mSplashScreenStyleSolidColor = true;
if (mStartingWindow != null) {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Finish starting %s"
@@ -6795,7 +6796,7 @@
void onStartingWindowDrawn() {
boolean wasTaskVisible = false;
if (task != null) {
- mAllowIconSplashScreen = false;
+ mSplashScreenStyleSolidColor = true;
wasTaskVisible = !setTaskHasBeenVisible();
}
@@ -7320,32 +7321,19 @@
}
/**
- * Checks whether an icon splash screen can be used in the starting window based on the
- * preference in the {@code options} and this activity's theme, giving higher priority to the
- * {@code options}'s preference.
- *
- * When no preference is specified, a default behaviour is defined:
- * - if the activity is started from the home or shell app, an icon can be used
- * - if the activity is started from SystemUI, an icon should not be used
- * - if there is a launching activity, use its preference
- * - if none of the above is met, only use an icon when the activity is started for the first
- * time from a System app
- *
- * The returned value is sent to WmShell, which will make the final decision on what splash
- * screen type will be used.
- *
- * @return true if an icon can be used in the splash screen
- * false when an icon should not be used in the splash screen
+ * @return true if a solid color splash screen must be used
+ * false when an icon splash screen can be used, but the final decision for whether to
+ * use an icon or solid color splash screen will be made by WmShell.
*/
- private boolean canUseIconSplashScreen(ActivityRecord sourceRecord,
+ private boolean shouldUseSolidColorSplashScreen(ActivityRecord sourceRecord,
boolean startActivity, ActivityOptions options, int resolvedTheme) {
if (sourceRecord == null && !startActivity) {
- // Shouldn't use an icon if this activity is not top activity. This could happen when
- // adding a splash screen window to the warm start activity which is re-create because
- // top is finishing.
+ // Use simple style if this activity is not top activity. This could happen when adding
+ // a splash screen window to the warm start activity which is re-create because top is
+ // finishing.
final ActivityRecord above = task.getActivityAbove(this);
if (above != null) {
- return false;
+ return true;
}
}
@@ -7353,33 +7341,32 @@
final int optionsStyle = options != null ? options.getSplashScreenStyle() :
SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR) {
- return false;
+ return true;
} else if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_ICON
|| isIconStylePreferred(resolvedTheme)) {
- return true;
+ return false;
}
// Choose the default behavior when neither the ActivityRecord nor the activity theme have
// specified a splash screen style.
if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME || launchedFromUid == Process.SHELL_UID) {
- return true;
- } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
return false;
+ } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
+ return true;
} else {
- // Need to check sourceRecord in case this activity is launched from a service or a
- // trampoline activity.
+ // Need to check sourceRecord in case this activity is launched from a service.
if (sourceRecord == null) {
sourceRecord = searchCandidateLaunchingActivity();
}
if (sourceRecord != null) {
- return sourceRecord.mAllowIconSplashScreen;
+ return sourceRecord.mSplashScreenStyleSolidColor;
}
// Use an icon if the activity was launched from System for the first start.
- // Otherwise, can't use an icon splash screen.
- return mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEM && startActivity;
+ // Otherwise, must use solid color splash screen.
+ return mLaunchSourceType != LAUNCH_SOURCE_TYPE_SYSTEM || !startActivity;
}
}
@@ -7443,7 +7430,7 @@
final int resolvedTheme = evaluateStartingWindowTheme(prev, packageName, theme,
splashScreenTheme);
- mAllowIconSplashScreen = canUseIconSplashScreen(sourceRecord, startActivity,
+ mSplashScreenStyleSolidColor = shouldUseSolidColorSplashScreen(sourceRecord, startActivity,
startOptions, resolvedTheme);
final boolean activityCreated =
@@ -7455,7 +7442,7 @@
final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
prev, newTask || newSingleActivity, taskSwitch, processRunning,
- allowTaskSnapshot(), activityCreated, mAllowIconSplashScreen, allDrawn);
+ allowTaskSnapshot(), activityCreated, mSplashScreenStyleSolidColor, allDrawn);
if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) {
Slog.d(TAG, "Scheduled starting window for " + this);
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 184de58..10405ec 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -64,6 +64,7 @@
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -301,12 +302,9 @@
}
// Always update the reordering time when this is called to ensure that the timeout
- // is reset. Extend this duration when running in tests.
- final long timeout = ActivityManager.isRunningInUserTestHarness()
- ? mFreezeTaskListTimeoutMs * 10
- : mFreezeTaskListTimeoutMs;
+ // is reset
mService.mH.removeCallbacks(mResetFreezeTaskListOnTimeoutRunnable);
- mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, timeout);
+ mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, mFreezeTaskListTimeoutMs);
}
/**
@@ -506,6 +504,16 @@
Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
List<Task> tasks = mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks);
+
+ // Tasks are ordered from most recent to least recent. Update the last active time to be
+ // in sync with task recency when device reboots, so the most recent task has the
+ // highest last active time
+ long currentElapsedTime = SystemClock.elapsedRealtime();
+ for (int i = 0; i < tasks.size(); i++) {
+ Task task = tasks.get(i);
+ task.lastActiveTime = currentElapsedTime - i;
+ }
+
mTasks.addAll(tasks);
cleanupLocked(userId);
mUsersWithRecentsLoaded.put(userId, true);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2a33918..ea5c9c2 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2234,9 +2234,11 @@
mService.getTaskChangeNotificationController().notifyActivityUnpinned();
}
mWindowManager.mPolicy.setPipVisibilityLw(inPip);
- mWmService.mTransactionFactory.get()
- .setTrustedOverlay(task.getSurfaceControl(), inPip)
- .apply();
+ if (task.getSurfaceControl() != null) {
+ mWmService.mTransactionFactory.get()
+ .setTrustedOverlay(task.getSurfaceControl(), inPip)
+ .apply();
+ }
}
void executeAppTransitionForAllDisplay() {
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index a0517be..a55c232 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -19,12 +19,12 @@
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_ICON;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SNAPSHOT;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
@@ -102,7 +102,7 @@
static int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch,
boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated,
- boolean allowIcon, boolean useLegacy, boolean activityDrawn, int startingWindowType,
+ boolean isSolidColor, boolean useLegacy, boolean activityDrawn, int startingWindowType,
String packageName, int userId) {
int parameter = 0;
if (newTask) {
@@ -120,8 +120,8 @@
if (activityCreated || startingWindowType == STARTING_WINDOW_TYPE_SNAPSHOT) {
parameter |= TYPE_PARAMETER_ACTIVITY_CREATED;
}
- if (allowIcon) {
- parameter |= TYPE_PARAMETER_ALLOW_ICON;
+ if (isSolidColor) {
+ parameter |= TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
}
if (useLegacy) {
parameter |= TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 261fc2e..8385615 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1162,11 +1162,17 @@
forAllActivities(oldParentTask::cleanUpActivityReferences);
}
- if (oldParent.inPinnedWindowingMode()
- && (newParent == null || !newParent.inPinnedWindowingMode())) {
- // Notify if a task from the root pinned task is being removed
- // (or moved depending on the mode).
- mRootWindowContainer.notifyActivityPipModeChanged(this, null);
+ if (newParent == null || !newParent.inPinnedWindowingMode()) {
+ if (oldParent.inPinnedWindowingMode()) {
+ // Notify if a task from the root pinned task is being removed
+ // (or moved depending on the mode).
+ mRootWindowContainer.notifyActivityPipModeChanged(this, null);
+ } else if (inPinnedWindowingMode()) {
+ // The task in pinned mode is removed, even though the old parent was not pinned
+ // The task was most likely force killed or crashed
+ Slog.e(TAG, "Pinned task is removed t=" + this);
+ mRootWindowContainer.notifyActivityPipModeChanged(this, null);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 5d95bc7..50bc825 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -255,6 +255,11 @@
boolean mClearedForReorderActivityToFront;
/**
+ * Whether the TaskFragment surface is managed by a system {@link TaskFragmentOrganizer}.
+ */
+ boolean mIsSurfaceManagedBySystemOrganizer = false;
+
+ /**
* When we are in the process of pausing an activity, before starting the
* next one, this variable holds the activity that is currently being paused.
*
@@ -449,13 +454,21 @@
void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
@NonNull String processName) {
+ setTaskFragmentOrganizer(organizer, uid, processName,
+ false /* isSurfaceManagedBySystemOrganizer */);
+ }
+
+ void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
+ @NonNull String processName, boolean isSurfaceManagedBySystemOrganizer) {
mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(organizer.asBinder());
mTaskFragmentOrganizerUid = uid;
mTaskFragmentOrganizerProcessName = processName;
+ mIsSurfaceManagedBySystemOrganizer = isSurfaceManagedBySystemOrganizer;
}
void onTaskFragmentOrganizerRemoved() {
mTaskFragmentOrganizer = null;
+ mIsSurfaceManagedBySystemOrganizer = false;
}
/** Whether this TaskFragment is organized by the given {@code organizer}. */
@@ -2396,6 +2409,9 @@
if (mDelayOrganizedTaskFragmentSurfaceUpdate || mTaskFragmentOrganizer == null) {
return;
}
+ if (mIsSurfaceManagedBySystemOrganizer) {
+ return;
+ }
if (mTransitionController.isShellTransitionsEnabled()
&& !mTransitionController.isCollecting(this)) {
// TaskFragmentOrganizer doesn't have access to the surface for security reasons, so
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index ea722b6..04164c2 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -26,6 +26,7 @@
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
@@ -50,12 +51,15 @@
import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOperation;
+import android.window.TaskFragmentOrganizerToken;
import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.window.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -133,6 +137,13 @@
new WeakHashMap<>();
/**
+ * Whether this {@link android.window.TaskFragmentOrganizer} is a system organizer. If true,
+ * the {@link android.view.SurfaceControl} of the {@link TaskFragment} is provided to the
+ * client in the {@link TYPE_TASK_FRAGMENT_APPEARED} event.
+ */
+ private final boolean mIsSystemOrganizer;
+
+ /**
* {@link RemoteAnimationDefinition} for embedded activities transition animation that is
* organized by this organizer.
*/
@@ -147,10 +158,12 @@
*/
private final ArrayMap<IBinder, Integer> mDeferredTransitions = new ArrayMap<>();
- TaskFragmentOrganizerState(ITaskFragmentOrganizer organizer, int pid, int uid) {
+ TaskFragmentOrganizerState(@NonNull ITaskFragmentOrganizer organizer, int pid, int uid,
+ boolean isSystemOrganizer) {
mOrganizer = organizer;
mOrganizerPid = pid;
mOrganizerUid = uid;
+ mIsSystemOrganizer = isSystemOrganizer;
try {
mOrganizer.asBinder().linkToDeath(this, 0 /*flags*/);
} catch (RemoteException e) {
@@ -235,11 +248,15 @@
tf.mTaskFragmentAppearedSent = true;
mLastSentTaskFragmentInfos.put(tf, info);
mTaskFragmentTaskIds.put(tf, taskId);
- return new TaskFragmentTransaction.Change(
+ final TaskFragmentTransaction.Change change = new TaskFragmentTransaction.Change(
TYPE_TASK_FRAGMENT_APPEARED)
.setTaskFragmentToken(tf.getFragmentToken())
.setTaskFragmentInfo(info)
.setTaskId(taskId);
+ if (mIsSystemOrganizer) {
+ change.setTaskFragmentSurfaceControl(tf.getSurfaceControl());
+ }
+ return change;
}
@NonNull
@@ -435,8 +452,25 @@
: null;
}
+ @VisibleForTesting
+ void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
+ registerOrganizerInternal(organizer, false /* isSystemOrganizer */);
+ }
+
@Override
- public void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
+ public void registerOrganizer(
+ @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
+ registerOrganizerInternal(
+ organizer,
+ Flags.taskFragmentSystemOrganizerFlag() && isSystemOrganizer);
+ }
+
+ @VisibleForTesting
+ void registerOrganizerInternal(
+ @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
+ if (isSystemOrganizer) {
+ enforceTaskPermission("registerSystemOrganizer()");
+ }
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
synchronized (mGlobalLock) {
@@ -448,7 +482,7 @@
"Replacing existing organizer currently unsupported");
}
mTaskFragmentOrganizerState.put(organizer.asBinder(),
- new TaskFragmentOrganizerState(organizer, pid, uid));
+ new TaskFragmentOrganizerState(organizer, pid, uid, isSystemOrganizer));
mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
}
}
@@ -711,6 +745,12 @@
}
}
+ boolean isSystemOrganizer(@NonNull TaskFragmentOrganizerToken token) {
+ final TaskFragmentOrganizerState state =
+ mTaskFragmentOrganizerState.get(token.asBinder());
+ return state != null && state.mIsSystemOrganizer;
+ }
+
@Nullable
private PendingTaskFragmentEvent getLastPendingParentInfoChangedEvent(
@NonNull ITaskFragmentOrganizer organizer, @NonNull Task task) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3faf8b9..3db7765 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -9646,15 +9646,4 @@
Binder.restoreCallingIdentity(origId);
}
}
-
- /**
- * Resets the spatial ordering of recents for testing purposes.
- */
- void resetFreezeRecentTaskListReordering() {
- if (!checkCallingPermission(MANAGE_ACTIVITY_TASKS,
- "resetFreezeRecentTaskListReordering()")) {
- throw new SecurityException("Requires MANAGE_ACTIVITY_TASKS permission");
- }
- mAtmService.getRecentTasks().resetFreezeTaskListReorderingOnTimeout();
- }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index fa9a65f..8fad950 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -142,8 +142,6 @@
return runReset(pw);
case "disable-blur":
return runSetBlurDisabled(pw);
- case "reset-freeze-recent-tasks":
- return runResetFreezeRecentTaskListReordering(pw);
case "shell":
return runWmShellCommand(pw);
default:
@@ -254,11 +252,6 @@
return 0;
}
- private int runResetFreezeRecentTaskListReordering(PrintWriter pw) throws RemoteException {
- mInternal.resetFreezeRecentTaskListReordering();
- return 0;
- }
-
private void printInitialDisplayDensity(PrintWriter pw , int displayId) {
try {
final int initialDensity = mInterface.getInitialDisplayDensity(displayId);
@@ -1499,8 +1492,6 @@
printLetterboxHelp(pw);
printMultiWindowConfigHelp(pw);
- pw.println(" reset-freeze-recent-tasks");
- pw.println(" Resets the spatial ordering of the recent tasks list");
pw.println(" reset [-d DISPLAY_ID]");
pw.println(" Reset all override settings.");
if (!IS_USER) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 6d7e297..376cad7 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -99,6 +99,7 @@
import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentOperation;
+import android.window.TaskFragmentOrganizerToken;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -1993,8 +1994,10 @@
creationParams.getFragmentToken(), true /* createdByOrganizer */);
// Set task fragment organizer immediately, since it might have to be notified about further
// actions.
- taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(),
- ownerActivity.getUid(), ownerActivity.info.processName);
+ TaskFragmentOrganizerToken organizerToken = creationParams.getOrganizer();
+ taskFragment.setTaskFragmentOrganizer(organizerToken,
+ ownerActivity.getUid(), ownerActivity.info.processName,
+ mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken));
final int position;
if (creationParams.getPairedPrimaryFragmentToken() != null) {
// When there is a paired primary TaskFragment, we want to place the new TaskFragment
diff --git a/services/core/jni/TEST_MAPPING b/services/core/jni/TEST_MAPPING
index eb9db70..7f7eb48 100644
--- a/services/core/jni/TEST_MAPPING
+++ b/services/core/jni/TEST_MAPPING
@@ -7,7 +7,6 @@
"name": "CtsVibratorTestCases",
"options": [
{"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/services/devicepolicy/TEST_MAPPING b/services/devicepolicy/TEST_MAPPING
index fccd1ec..0d5534b 100644
--- a/services/devicepolicy/TEST_MAPPING
+++ b/services/devicepolicy/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsDevicePolicyManagerTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"exclude-annotation": "androidx.test.filters.LargeTest"
diff --git a/services/net/OWNERS b/services/net/OWNERS
index 62c5737..c24680e9 100644
--- a/services/net/OWNERS
+++ b/services/net/OWNERS
@@ -1,2 +1,2 @@
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
diff --git a/services/tests/InputMethodSystemServerTests/TEST_MAPPING b/services/tests/InputMethodSystemServerTests/TEST_MAPPING
index cedbfd2b..de9f771 100644
--- a/services/tests/InputMethodSystemServerTests/TEST_MAPPING
+++ b/services/tests/InputMethodSystemServerTests/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "FrameworksInputMethodSystemServerTests",
"options": [
{"include-filter": "com.android.server.inputmethod"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
@@ -15,7 +14,6 @@
"name": "FrameworksImeTests",
"options": [
{"include-filter": "com.android.inputmethodservice"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
index 823ce45..0547719 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
@@ -112,4 +112,4 @@
@JvmStatic
fun data(): Array<Action> = Action.values()
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
index f085bd7..c44b2c5 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
@@ -43,8 +43,7 @@
@Parameterized.Parameter(0) lateinit var action: Action
@Before
- override fun setUp() {
- super.setUp()
+ fun setUp() {
if (action == Action.ON_USER_ADDED) {
createUserState(USER_ID_NEW)
}
@@ -881,4 +880,4 @@
@JvmStatic
fun data(): Array<Action> = Action.values()
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
new file mode 100644
index 0000000..e4e3368
--- /dev/null
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.permission.test
+
+import android.content.pm.PermissionGroupInfo
+import android.content.pm.PermissionInfo
+import com.android.server.permission.access.GetStateScope
+import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.permission.AppIdPermissionPolicy
+import com.android.server.permission.access.permission.Permission
+import com.android.server.permission.access.permission.PermissionFlags
+import com.android.server.testutils.mock
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+class AppIdPermissionPolicyTest : BaseAppIdPermissionPolicyTest() {
+ @Test
+ fun testOnAppIdRemoved_appIdIsRemoved_permissionFlagsCleared() {
+ val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0)
+ val permissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
+ )
+ val requestingPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+ )
+ addPackageState(permissionOwnerPackageState)
+ addPackageState(requestingPackageState)
+ addPermission(parsedPermission)
+ setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.INSTALL_GRANTED)
+
+ mutateState {
+ with(appIdPermissionPolicy) {
+ onAppIdRemoved(APP_ID_1)
+ }
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After onAppIdRemoved() is called for appId $APP_ID_1 that requests a permission" +
+ " owns by appId $APP_ID_0 with existing permission flags. The actual permission" +
+ " flags $actualFlags should be null"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnStorageVolumeMounted_nonSystemAppAfterNonSystemUpdate_remainsRevoked() {
+ val permissionOwnerPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+ val installedPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+ )
+ addPackageState(permissionOwnerPackageState)
+ addPackageState(installedPackageState)
+ addPermission(defaultPermission)
+ val oldFlags = PermissionFlags.INSTALL_REVOKED
+ setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags)
+
+ mutateState {
+ with(appIdPermissionPolicy) {
+ onStorageVolumeMounted(null, listOf(installedPackageState.packageName), false)
+ }
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After onStorageVolumeMounted() is called for a non-system app that requests a normal" +
+ " permission with existing INSTALL_REVOKED flag after a non-system-update" +
+ " (such as an OTA update), the actual permission flags should remain revoked." +
+ " The actual permission flags $actualFlags should match the expected flags" +
+ " $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageRemoved_packageIsRemoved_permissionDefinitionsAndStatesAreUpdated() {
+ val permissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(
+ PACKAGE_NAME_0,
+ requestedPermissions = setOf(PERMISSION_NAME_0),
+ permissions = listOf(defaultPermission)
+ )
+ )
+ val requestingPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+ )
+ addPackageState(permissionOwnerPackageState)
+ addPackageState(requestingPackageState)
+ addPermission(defaultPermission)
+ val oldFlags = PermissionFlags.INSTALL_GRANTED
+ setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0, oldFlags)
+ setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags)
+
+ mutateState {
+ removePackageState(permissionOwnerPackageState)
+ with(appIdPermissionPolicy) {
+ onPackageRemoved(PACKAGE_NAME_0, APP_ID_0)
+ }
+ }
+
+ assertWithMessage(
+ "After onPackageRemoved() is called for a permission owner, the permission" +
+ " definitions owned by this package should be removed"
+ )
+ .that(getPermission(PERMISSION_NAME_0))
+ .isNull()
+
+ val app0ActualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
+ val app0ExpectedNewFlags = 0
+ assertWithMessage(
+ "After onPackageRemoved() is called for a permission owner, the permission states of" +
+ " this app should be trimmed. The actual permission flags $app0ActualFlags should" +
+ " match the expected flags $app0ExpectedNewFlags"
+ )
+ .that(app0ActualFlags)
+ .isEqualTo(app0ExpectedNewFlags)
+
+ val app1ActualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val app1ExpectedNewFlags = PermissionFlags.INSTALL_REVOKED
+ assertWithMessage(
+ "After onPackageRemoved() is called for a permission owner, the permission states of" +
+ " the permission requester should remain unchanged. The actual permission flags" +
+ " $app1ActualFlags should match the expected flags $app1ExpectedNewFlags"
+ )
+ .that(app1ActualFlags)
+ .isEqualTo(app1ExpectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageInstalled_nonSystemAppIsInstalled_upgradeExemptFlagIsCleared() {
+ val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.UPGRADE_EXEMPT
+ testOnPackageInstalled(
+ oldFlags,
+ permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
+ ) {}
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.SOFT_RESTRICTED
+ assertWithMessage(
+ "After onPackageInstalled() is called for a non-system app that requests a runtime" +
+ " soft restricted permission, UPGRADE_EXEMPT flag should be removed. The actual" +
+ " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageInstalled_systemAppIsInstalled_upgradeExemptFlagIsRetained() {
+ val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.UPGRADE_EXEMPT
+ testOnPackageInstalled(
+ oldFlags,
+ permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED,
+ isInstalledPackageSystem = true
+ ) {}
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After onPackageInstalled() is called for a system app that requests a runtime" +
+ " soft restricted permission, UPGRADE_EXEMPT flag should be retained. The actual" +
+ " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageInstalled_requestedPermissionAlsoRequestedBySystemApp_exemptFlagIsRetained() {
+ val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.UPGRADE_EXEMPT
+ testOnPackageInstalled(
+ oldFlags,
+ permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
+ ) {
+ val systemAppPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_2, requestedPermissions = setOf(PERMISSION_NAME_0)),
+ isSystem = true
+ )
+ addPackageState(systemAppPackageState)
+ }
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After onPackageInstalled() is called for a non-system app that requests a runtime" +
+ " soft restricted permission, and that permission is also requested by a system" +
+ " app in the same appId, UPGRADE_EXEMPT flag should be retained. The actual" +
+ " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageInstalled_restrictedPermissionsNotExempt_getsRestrictionFlags() {
+ val oldFlags = PermissionFlags.RESTRICTION_REVOKED
+ testOnPackageInstalled(
+ oldFlags,
+ permissionInfoFlags = PermissionInfo.FLAG_HARD_RESTRICTED
+ ) {}
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After onPackageInstalled() is called for a non-system app that requests a runtime" +
+ " hard restricted permission that is not exempted. The actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageInstalled_restrictedPermissionsIsExempted_clearsRestrictionFlags() {
+ val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.INSTALLER_EXEMPT
+ testOnPackageInstalled(
+ oldFlags,
+ permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
+ ) {}
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.INSTALLER_EXEMPT
+ assertWithMessage(
+ "After onPackageInstalled() is called for a non-system app that requests a runtime" +
+ " soft restricted permission that is exempted. The actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ private fun testOnPackageInstalled(
+ oldFlags: Int,
+ permissionInfoFlags: Int = 0,
+ isInstalledPackageSystem: Boolean = false,
+ additionalSetup: () -> Unit
+ ) {
+ val parsedPermission = mockParsedPermission(
+ PERMISSION_NAME_0,
+ PACKAGE_NAME_0,
+ protectionLevel = PermissionInfo.PROTECTION_DANGEROUS,
+ flags = permissionInfoFlags
+ )
+ val permissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
+ )
+ addPackageState(permissionOwnerPackageState)
+ addPermission(parsedPermission)
+
+ additionalSetup()
+
+ mutateState {
+ val installedPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(
+ PACKAGE_NAME_1,
+ requestedPermissions = setOf(PERMISSION_NAME_0),
+ ),
+ isSystem = isInstalledPackageSystem,
+ )
+ addPackageState(installedPackageState, newState)
+ setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags, newState)
+ with(appIdPermissionPolicy) {
+ onPackageInstalled(installedPackageState, USER_ID_0)
+ }
+ }
+ }
+
+ @Test
+ fun testOnStateMutated_notEmpty_isCalledForEachListener() {
+ val mockListener = mock<AppIdPermissionPolicy.OnPermissionFlagsChangedListener> {}
+ appIdPermissionPolicy.addOnPermissionFlagsChangedListener(mockListener)
+
+ GetStateScope(oldState).apply {
+ with(appIdPermissionPolicy) {
+ onStateMutated()
+ }
+ }
+
+ verify(mockListener, times(1)).onStateMutated()
+ }
+
+ @Test
+ fun testGetPermissionTrees() {
+ val permissionTrees: IndexedMap<String, Permission>
+ GetStateScope(oldState).apply {
+ with(appIdPermissionPolicy) {
+ permissionTrees = getPermissionTrees()
+ }
+ }
+
+ assertThat(oldState.systemState.permissionTrees).isEqualTo(permissionTrees)
+ }
+
+ @Test
+ fun testFindPermissionTree() {
+ val permissionTree = createSimplePermission(isTree = true)
+ val actualPermissionTree: Permission?
+ oldState.mutateSystemState().mutatePermissionTrees()[PERMISSION_TREE_NAME] = permissionTree
+
+ GetStateScope(oldState).apply {
+ with(appIdPermissionPolicy) {
+ actualPermissionTree = findPermissionTree(PERMISSION_BELONGS_TO_A_TREE)
+ }
+ }
+
+ assertThat(actualPermissionTree).isEqualTo(permissionTree)
+ }
+
+ @Test
+ fun testAddPermissionTree() {
+ val permissionTree = createSimplePermission(isTree = true)
+
+ mutateState {
+ with(appIdPermissionPolicy) {
+ addPermissionTree(permissionTree)
+ }
+ }
+
+ assertThat(newState.systemState.permissionTrees[PERMISSION_TREE_NAME])
+ .isEqualTo(permissionTree)
+ }
+
+ @Test
+ fun testGetPermissionGroups() {
+ val permissionGroups: IndexedMap<String, PermissionGroupInfo>
+ GetStateScope(oldState).apply {
+ with(appIdPermissionPolicy) {
+ permissionGroups = getPermissionGroups()
+ }
+ }
+
+ assertThat(oldState.systemState.permissionGroups).isEqualTo(permissionGroups)
+ }
+
+ @Test
+ fun testGetPermissions() {
+ val permissions: IndexedMap<String, Permission>
+ GetStateScope(oldState).apply {
+ with(appIdPermissionPolicy) {
+ permissions = getPermissions()
+ }
+ }
+
+ assertThat(oldState.systemState.permissions).isEqualTo(permissions)
+ }
+
+ @Test
+ fun testAddPermission() {
+ val permission = createSimplePermission()
+
+ mutateState {
+ with(appIdPermissionPolicy) {
+ addPermission(permission)
+ }
+ }
+
+ assertThat(newState.systemState.permissions[PERMISSION_NAME_0]).isEqualTo(permission)
+ }
+
+ @Test
+ fun testRemovePermission() {
+ val permission = createSimplePermission()
+
+ mutateState {
+ with(appIdPermissionPolicy) {
+ addPermission(permission)
+ removePermission(permission)
+ }
+ }
+
+ assertThat(newState.systemState.permissions[PERMISSION_NAME_0]).isNull()
+ }
+
+ @Test
+ fun testGetUidPermissionFlags() {
+ val uidPermissionFlags: IndexedMap<String, Int>?
+ GetStateScope(oldState).apply {
+ with(appIdPermissionPolicy) {
+ uidPermissionFlags = getUidPermissionFlags(APP_ID_0, USER_ID_0)
+ }
+ }
+
+ assertThat(oldState.userStates[USER_ID_0]!!.appIdPermissionFlags[APP_ID_0])
+ .isEqualTo(uidPermissionFlags)
+ }
+
+ @Test
+ fun testUpdateAndGetPermissionFlags() {
+ val flags = PermissionFlags.INSTALL_GRANTED
+ var actualFlags = 0
+ mutateState {
+ with(appIdPermissionPolicy) {
+ updatePermissionFlags(
+ APP_ID_0,
+ USER_ID_0,
+ PERMISSION_NAME_0,
+ PermissionFlags.MASK_ALL,
+ flags
+ )
+ actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
+ }
+ }
+
+ assertThat(actualFlags).isEqualTo(flags)
+ }
+}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
index 7966c5c..ec84bc3 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
@@ -34,7 +34,6 @@
import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.permission.AppIdPermissionPolicy
import com.android.server.permission.access.permission.Permission
-import com.android.server.permission.access.permission.PermissionFlags
import com.android.server.permission.access.util.hasBits
import com.android.server.pm.parsing.PackageInfoUtils
import com.android.server.pm.pkg.AndroidPackage
@@ -45,10 +44,8 @@
import com.android.server.testutils.any
import com.android.server.testutils.mock
import com.android.server.testutils.whenever
-import com.google.common.truth.Truth.assertWithMessage
import org.junit.Before
import org.junit.Rule
-import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyLong
@@ -56,7 +53,7 @@
* Mocking unit test for AppIdPermissionPolicy.
*/
@RunWith(AndroidJUnit4::class)
-open class BaseAppIdPermissionPolicyTest {
+abstract class BaseAppIdPermissionPolicyTest {
protected lateinit var oldState: MutableAccessState
protected lateinit var newState: MutableAccessState
@@ -80,7 +77,7 @@
.build()
@Before
- open fun setUp() {
+ fun baseSetUp() {
oldState = MutableAccessState()
createUserState(USER_ID_0)
oldState.mutateExternalState().setPackageStates(ArrayMap())
@@ -139,78 +136,6 @@
}
}
- @Test
- fun testOnAppIdRemoved_appIdIsRemoved_permissionFlagsCleared() {
- val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0)
- val permissionOwnerPackageState = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
- )
- val requestingPackageState = mockPackageState(
- APP_ID_1,
- mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
- )
- addPackageState(permissionOwnerPackageState)
- addPackageState(requestingPackageState)
- addPermission(parsedPermission)
- setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.INSTALL_GRANTED)
-
- mutateState {
- with(appIdPermissionPolicy) {
- onAppIdRemoved(APP_ID_1)
- }
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = 0
- assertWithMessage(
- "After onAppIdRemoved() is called for appId $APP_ID_1 that requests a permission" +
- " owns by appId $APP_ID_0 with existing permission flags. The actual permission" +
- " flags $actualFlags should be null"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageRemoved_packageIsRemoved_permissionsAreTrimmedAndStatesAreEvaluated() {
- // TODO
- // shouldn't reuse test cases because it's really different despite it's also for
- // trim permission states. It's different because it's package removal
- }
-
- @Test
- fun testOnPackageInstalled_nonSystemAppIsInstalled_upgradeExemptFlagIsCleared() {
- // TODO
- // should be fine for it to be its own test cases and not to re-use
- // clearRestrictedPermissionImplicitExemption
- }
-
- @Test
- fun testOnPackageInstalled_systemAppIsInstalled_upgradeExemptFlagIsRetained() {
- // TODO
- }
-
- @Test
- fun testOnPackageInstalled_requestedPermissionAlsoRequestedBySystemApp_exemptFlagIsRetained() {
- // TODO
- }
-
- @Test
- fun testOnPackageInstalled_restrictedPermissionsNotExempt_getsRestrictionFlags() {
- // TODO
- }
-
- @Test
- fun testOnPackageInstalled_restrictedPermissionsIsExempted_clearsRestrictionFlags() {
- // TODO
- }
-
- @Test
- fun testOnStateMutated_notEmpty_isCalledForEachListener() {
- // TODO
- }
-
/**
* Mock an AndroidPackage with PACKAGE_NAME_0, PERMISSION_NAME_0 and PERMISSION_GROUP_NAME_0
*/
@@ -221,6 +146,15 @@
permissions = listOf(defaultPermissionTree, defaultPermission)
)
+ protected fun createSimplePermission(isTree: Boolean = false): Permission {
+ val parsedPermission = if (isTree) { defaultPermissionTree } else { defaultPermission }
+ val permissionInfo = PackageInfoUtils.generatePermissionInfo(
+ parsedPermission,
+ PackageManager.GET_META_DATA.toLong()
+ )!!
+ return Permission(permissionInfo, true, Permission.TYPE_MANIFEST, APP_ID_0)
+ }
+
protected inline fun mutateState(action: MutateStateScope.() -> Unit) {
newState = oldState.toMutable()
MutateStateScope(oldState, newState).action()
@@ -330,15 +264,26 @@
) {
state.mutateExternalState().apply {
setPackageStates(
- packageStates.toMutableMap().apply {
- put(packageState.packageName, packageState)
- }
+ packageStates.toMutableMap().apply { put(packageState.packageName, packageState) }
)
mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
.add(packageState.packageName)
}
}
+ protected fun removePackageState(
+ packageState: PackageState,
+ state: MutableAccessState = oldState
+ ) {
+ state.mutateExternalState().apply {
+ setPackageStates(
+ packageStates.toMutableMap().apply { remove(packageState.packageName) }
+ )
+ mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
+ .remove(packageState.packageName)
+ }
+ }
+
protected fun addDisabledSystemPackageState(
packageState: PackageState,
state: MutableAccessState = oldState
@@ -429,6 +374,7 @@
@JvmStatic protected val PERMISSION_NAME_0 = "permissionName0"
@JvmStatic protected val PERMISSION_NAME_1 = "permissionName1"
@JvmStatic protected val PERMISSION_NAME_2 = "permissionName2"
+ @JvmStatic protected val PERMISSION_BELONGS_TO_A_TREE = "permissionTree.permission"
@JvmStatic protected val PERMISSION_READ_EXTERNAL_STORAGE =
Manifest.permission.READ_EXTERNAL_STORAGE
@JvmStatic protected val PERMISSION_POST_NOTIFICATIONS =
diff --git a/services/tests/RemoteProvisioningServiceTests/OWNERS b/services/tests/RemoteProvisioningServiceTests/OWNERS
index 348f940..ea6dc72 100644
--- a/services/tests/RemoteProvisioningServiceTests/OWNERS
+++ b/services/tests/RemoteProvisioningServiceTests/OWNERS
@@ -1 +1 @@
-file:platform/frameworks/base:master:/core/java/android/security/rkp/OWNERS
+file:platform/frameworks/base:main:/core/java/android/security/rkp/OWNERS
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java
index 7a4327c..2fd6e5f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java
@@ -19,7 +19,6 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.isA;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -28,18 +27,15 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.database.ContentObserver;
-import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.net.Uri;
import android.os.Handler;
-import android.os.PowerManager;
import android.os.UserHandle;
import android.os.test.TestLooper;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.view.Display;
-import android.view.DisplayAdjustments;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
@@ -63,7 +59,6 @@
private static final float EPSILON = 0.00001f;
private static final Uri BRIGHTNESS_URI =
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
- private static final float BRIGHTNESS_MAX = 0.6f;
private Context mContext;
private MockContentResolver mContentResolverSpy;
@@ -71,7 +66,6 @@
private DisplayListener mDisplayListener;
private ContentObserver mContentObserver;
private TestLooper mTestLooper;
- private BrightnessSynchronizer mSynchronizer;
@Mock private DisplayManager mDisplayManagerMock;
@Captor private ArgumentCaptor<DisplayListener> mDisplayListenerCaptor;
@@ -80,17 +74,7 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
-
- Display display = mock(Display.class);
- when(display.getDisplayAdjustments()).thenReturn(new DisplayAdjustments());
- BrightnessInfo info = new BrightnessInfo(PowerManager.BRIGHTNESS_INVALID_FLOAT,
- PowerManager.BRIGHTNESS_MIN, BRIGHTNESS_MAX,
- BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, BRIGHTNESS_MAX,
- BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
- when(display.getBrightnessInfo()).thenReturn(info);
-
- mContext = spy(new ContextWrapper(
- ApplicationProvider.getApplicationContext().createDisplayContext(display)));
+ mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mContentResolverSpy = spy(new MockContentResolver(mContext));
mContentResolverSpy.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mContentResolverSpy);
@@ -144,12 +128,13 @@
@Test
public void testSetSameIntValue_nothingUpdated() {
putFloatSetting(0.5f);
+ putIntSetting(128);
start();
- putIntSetting(fToI(0.5f));
+ putIntSetting(128);
advanceTime(10);
verify(mDisplayManagerMock, times(0)).setBrightness(
- eq(Display.DEFAULT_DISPLAY), eq(0.5f));
+ eq(Display.DEFAULT_DISPLAY), eq(iToF(128)));
}
@Test
@@ -169,13 +154,14 @@
// Verify that this update did not get sent to float, because synchronizer
// is still waiting for confirmation of its first value.
verify(mDisplayManagerMock, times(0)).setBrightness(
- Display.DEFAULT_DISPLAY, iToF(20));
+ eq(Display.DEFAULT_DISPLAY), eq(iToF(20)));
// Send the confirmation of the initial change. This should trigger the new value to
// finally be processed and we can verify that the new value (20) is sent.
putIntSetting(fToI(0.4f));
advanceTime(10);
- verify(mDisplayManagerMock).setBrightness(Display.DEFAULT_DISPLAY, iToF(20));
+ verify(mDisplayManagerMock).setBrightness(
+ eq(Display.DEFAULT_DISPLAY), eq(iToF(20)));
}
@@ -197,7 +183,8 @@
advanceTime(200);
// Verify that the new value gets sent because the timeout expired.
- verify(mDisplayManagerMock).setBrightness(Display.DEFAULT_DISPLAY, iToF(20));
+ verify(mDisplayManagerMock).setBrightness(
+ eq(Display.DEFAULT_DISPLAY), eq(iToF(20)));
// Send a confirmation of the initial event, BrightnessSynchronizer should treat this as a
// new event because the timeout had already expired
@@ -209,14 +196,14 @@
// Verify we sent what would have been the confirmation as a new event to displaymanager.
// We do both fToI and iToF because the conversions are not symmetric.
- verify(mDisplayManagerMock).setBrightness(Display.DEFAULT_DISPLAY,
- iToF(fToI(0.4f)));
+ verify(mDisplayManagerMock).setBrightness(
+ eq(Display.DEFAULT_DISPLAY), eq(iToF(fToI(0.4f))));
}
- private void start() {
- mSynchronizer = new BrightnessSynchronizer(mContext, mTestLooper.getLooper(),
+ private BrightnessSynchronizer start() {
+ BrightnessSynchronizer bs = new BrightnessSynchronizer(mContext, mTestLooper.getLooper(),
mClock::now);
- mSynchronizer.startSynchronizing();
+ bs.startSynchronizing();
verify(mDisplayManagerMock).registerDisplayListener(mDisplayListenerCaptor.capture(),
isA(Handler.class), eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
mDisplayListener = mDisplayListenerCaptor.getValue();
@@ -224,6 +211,7 @@
verify(mContentResolverSpy).registerContentObserver(eq(BRIGHTNESS_URI), eq(false),
mContentObserverCaptor.capture(), eq(UserHandle.USER_ALL));
mContentObserver = mContentObserverCaptor.getValue();
+ return bs;
}
private int getIntSetting() throws Exception {
@@ -253,11 +241,11 @@
}
private int fToI(float brightness) {
- return mSynchronizer.brightnessFloatToIntSetting(brightness);
+ return BrightnessSynchronizer.brightnessFloatToInt(brightness);
}
private float iToF(int brightness) {
- return mSynchronizer.brightnessIntSettingToFloat(brightness);
+ return BrightnessSynchronizer.brightnessIntToFloat(brightness);
}
private void advanceTime(long timeMs) {
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 306de52..2396905 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -71,7 +71,6 @@
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessConfiguration;
-import android.hardware.display.BrightnessInfo;
import android.hardware.display.Curve;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
@@ -95,7 +94,6 @@
import android.os.RemoteException;
import android.view.ContentRecordingSession;
import android.view.Display;
-import android.view.DisplayAdjustments;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
@@ -103,6 +101,7 @@
import android.view.SurfaceControl;
import android.window.DisplayWindowPolicyController;
+import androidx.test.InstrumentationRegistry;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
@@ -333,11 +332,7 @@
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
// TODO: b/287945043
- Display display = mock(Display.class);
- when(display.getDisplayAdjustments()).thenReturn(new DisplayAdjustments());
- when(display.getBrightnessInfo()).thenReturn(mock(BrightnessInfo.class));
- mContext = spy(new ContextWrapper(
- ApplicationProvider.getApplicationContext().createDisplayContext(display)));
+ mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mResources = Mockito.spy(mContext.getResources());
manageDisplaysPermission(/* granted= */ false);
when(mContext.getResources()).thenReturn(mResources);
@@ -1912,6 +1907,7 @@
@Test
public void testSettingTwoBrightnessConfigurationsOnMultiDisplay() {
+ Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
// get the first two internal displays
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
index c3322ec..c63fac9 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
@@ -118,7 +118,8 @@
mClock.fastForward(3000);
mTestHandler.timeAdvance();
assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
- assertEquals(0.1f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); // (1-0.6) / 4
+ // 0.6 to HLG = 0.905727, rate = (1-0.905727) / 4
+ assertEquals(0.023568f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
}
@Test
@@ -146,7 +147,8 @@
mClock.fastForward(1000);
mTestHandler.timeAdvance();
assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
- assertEquals(0.2f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); // (1-0.6) / 2
+ // 0.6 to HLG = 0.905727, rate = (1-0.905727) / 2
+ assertEquals(0.047137f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
}
@Test
@@ -173,7 +175,8 @@
mClock.fastForward(3000);
mTestHandler.timeAdvance();
assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
- assertEquals(0.1f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
+ // 0.6 to HLG = 0.905727, rate = (1-0.905727) / 4
+ assertEquals(0.023568f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
}
// MsgInfo.sendTime is calculated first by adding SystemClock.uptimeMillis()
diff --git a/services/tests/dreamservicetests/TEST_MAPPING b/services/tests/dreamservicetests/TEST_MAPPING
index d73d83d..a644ea6 100644
--- a/services/tests/dreamservicetests/TEST_MAPPING
+++ b/services/tests/dreamservicetests/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "DreamServiceTests",
"options": [
{"include-filter": "com.android.server.dreams"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/services/tests/powerstatstests/TEST_MAPPING b/services/tests/powerstatstests/TEST_MAPPING
index e1eb1e4..eee68a4 100644
--- a/services/tests/powerstatstests/TEST_MAPPING
+++ b/services/tests/powerstatstests/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "PowerStatsTests",
"options": [
{"include-filter": "com.android.server.power.stats"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
index a508718..b69b554 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -125,6 +125,8 @@
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
anyLong(), anyInt(), eq(requireConfirmation),
eq(targetUserId), any());
+
+ verify(mAuthenticationStatsCollector).authenticate(eq(targetUserId), eq(authenticated));
}
@Test
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
new file mode 100644
index 0000000..a7c8a6c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -0,0 +1,890 @@
+/*
+ * 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.companion.virtual;
+
+import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+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.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.WindowConfiguration;
+import android.companion.virtual.IVirtualDeviceIntentInterceptor;
+import android.companion.virtual.VirtualDeviceManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.util.ArraySet;
+import android.view.Display;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.app.BlockedAppStreamingActivity;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class GenericWindowPolicyControllerTest {
+
+ private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY + 1;
+ private static final int TEST_UID = 1234567;
+ private static final String DISPLAY_CATEGORY = "com.display.category";
+ private static final String NONBLOCKED_APP_PACKAGE_NAME = "com.someapp";
+ private static final String BLOCKED_PACKAGE_NAME = "com.blockedapp";
+ private static final int FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES = 0x00000;
+ private static final String TEST_SITE = "http://test";
+ private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT =
+ new ComponentName("android", BlockedAppStreamingActivity.class.getName());
+ private static final ComponentName BLOCKED_COMPONENT = new ComponentName(BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME);
+ private static final ComponentName NONBLOCKED_COMPONENT = new ComponentName(
+ NONBLOCKED_APP_PACKAGE_NAME, NONBLOCKED_APP_PACKAGE_NAME);
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Mock
+ private GenericWindowPolicyController.PipBlockedCallback mPipBlockedCallback;
+ @Mock
+ private VirtualDeviceManager.ActivityListener mActivityListener;
+ @Mock
+ private GenericWindowPolicyController.IntentListenerCallback mIntentListenerCallback;
+ @Mock
+ private GenericWindowPolicyController.ActivityBlockedCallback mActivityBlockedCallback;
+ @Mock
+ private GenericWindowPolicyController.RunningAppsChangedListener mRunningAppsChangedListener;
+ @Mock
+ private GenericWindowPolicyController.SecureWindowCallback mSecureWindowCallback;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void showTasksInHostDeviceRecents() {
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ gwpc.setShowInHostDeviceRecents(true);
+ assertThat(gwpc.canShowTasksInHostDeviceRecents()).isTrue();
+
+ gwpc.setShowInHostDeviceRecents(false);
+ assertThat(gwpc.canShowTasksInHostDeviceRecents()).isFalse();
+ }
+
+ @Test
+ public void containsUid() {
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ assertThat(gwpc.containsUid(TEST_UID)).isFalse();
+
+ gwpc.onRunningAppsChanged(new ArraySet<>(Arrays.asList(TEST_UID)));
+ assertThat(gwpc.containsUid(TEST_UID)).isTrue();
+
+ gwpc.onRunningAppsChanged(new ArraySet<>());
+ assertThat(gwpc.containsUid(TEST_UID)).isFalse();
+ }
+
+ @Test
+ public void isEnteringPipAllowed_falseByDefault() {
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ assertThat(gwpc.isEnteringPipAllowed(TEST_UID)).isFalse();
+ verify(mPipBlockedCallback).onEnteringPipBlocked(TEST_UID);
+ }
+
+ @Test
+ public void isEnteringPipAllowed_dpcSupportsPinned_allowed() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setSupportedWindowingModes(new HashSet<>(
+ Arrays.asList(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
+ WindowConfiguration.WINDOWING_MODE_PINNED)));
+ assertThat(gwpc.isEnteringPipAllowed(TEST_UID)).isTrue();
+ verify(mPipBlockedCallback, never()).onEnteringPipBlocked(TEST_UID);
+ }
+
+ @Test
+ public void openNonBlockedAppOnVirtualDisplay_isNotBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void activityDoesNotSupportDisplayOnRemoteDevices_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ false,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void openBlockedComponentOnVirtualDisplay_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithBlockedComponent(BLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void addActivityPolicyExemption_openBlockedOnVirtualDisplay_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+ gwpc.setActivityLaunchDefaultAllowed(true);
+ gwpc.addActivityPolicyExemption(BLOCKED_COMPONENT);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void openNotAllowedComponentOnBlocklistVirtualDisplay_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void addActivityPolicyExemption_openNotAllowedOnVirtualDisplay_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+ gwpc.setActivityLaunchDefaultAllowed(false);
+ gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void openAllowedComponentOnBlocklistVirtualDisplay_startsActivity() {
+ GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void addActivityPolicyExemption_openAllowedOnVirtualDisplay_startsActivity() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+ gwpc.setActivityLaunchDefaultAllowed(false);
+ gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_mismatchingUserHandle_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null,
+ /* uid */ UserHandle.PER_USER_RANGE + 1);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_blockedAppStreamingComponent_isNeverBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+ BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_blockedAppStreamingComponentExplicitlyBlocked_isNeverBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithBlockedComponent(
+ BLOCKED_APP_STREAMING_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+ BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_blockedAppStreamingComponentExemptFromStreaming_isNeverBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+ gwpc.setActivityLaunchDefaultAllowed(true);
+ gwpc.addActivityPolicyExemption(BLOCKED_APP_STREAMING_COMPONENT);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+ BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_blockedAppStreamingComponentNotAllowlisted_isNeverBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+ BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_blockedAppStreamingComponentNotExemptFromBlocklist_isNeverBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+ gwpc.setActivityLaunchDefaultAllowed(false);
+ gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+ BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_customDisplayCategoryMatches_isNotBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithDisplayCategory(DISPLAY_CATEGORY);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ DISPLAY_CATEGORY);
+
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_customDisplayCategoryDoesNotMatch_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithDisplayCategory(DISPLAY_CATEGORY);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ "some.random.category");
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_crossTaskLaunch_fromDefaultDisplay_isNotBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityCanBeLaunched(gwpc, Display.DEFAULT_DISPLAY, true,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_notExplicitlyBlocked_isNotBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationBlockedFor(
+ BLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertActivityCanBeLaunched(gwpc, DISPLAY_ID, true,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_explicitlyBlocked_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationBlockedFor(
+ BLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, DISPLAY_ID, true,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_notAllowed_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationAllowed(
+ NONBLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, DISPLAY_ID, true,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_allowed_isNotBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationAllowed(
+ NONBLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityCanBeLaunched(gwpc, DISPLAY_ID, true,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_unsupportedWindowingMode_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, DISPLAY_ID, true, WindowConfiguration.WINDOWING_MODE_PINNED,
+ activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_permissionComponent_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithPermissionComponent(BLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void registerRunningAppsChangedListener_onRunningAppsChanged_listenersNotified() {
+ ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(TEST_UID));
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ gwpc.registerRunningAppsChangedListener(mRunningAppsChangedListener);
+ gwpc.onRunningAppsChanged(uids);
+
+ assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(1);
+ verify(mRunningAppsChangedListener).onRunningAppsChanged(uids);
+ }
+
+ @Test
+ public void onRunningAppsChanged_empty_onDisplayEmpty() {
+ ArraySet<Integer> uids = new ArraySet<>();
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ gwpc.onRunningAppsChanged(uids);
+
+ assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0);
+ verify(mActivityListener).onDisplayEmpty(DISPLAY_ID);
+ }
+
+ @Test
+ public void noRunningAppsChangedListener_onRunningAppsChanged_doesNotThrowException() {
+ ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(TEST_UID));
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ gwpc.onRunningAppsChanged(uids);
+
+ assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0);
+ verify(mRunningAppsChangedListener, never()).onRunningAppsChanged(uids);
+ }
+
+ @Test
+ public void registerUnregisterRunningAppsChangedListener_onRunningAppsChanged_doesNotThrowException() {
+ ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(TEST_UID));
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ gwpc.registerRunningAppsChangedListener(mRunningAppsChangedListener);
+ gwpc.unregisterRunningAppsChangedListener(mRunningAppsChangedListener);
+ gwpc.onRunningAppsChanged(uids);
+
+ assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0);
+ verify(mRunningAppsChangedListener, never()).onRunningAppsChanged(uids);
+ }
+
+ @Test
+ public void canActivityBeLaunched_intentInterceptedWhenRegistered_activityNoLaunch()
+ throws RemoteException {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(TEST_SITE));
+
+ IVirtualDeviceIntentInterceptor.Stub interceptor =
+ mock(IVirtualDeviceIntentInterceptor.Stub.class);
+ doNothing().when(interceptor).onIntentIntercepted(any());
+ doReturn(interceptor).when(interceptor).asBinder();
+ doReturn(interceptor).when(interceptor).queryLocalInterface(anyString());
+
+ GenericWindowPolicyController gwpc = createGwpc();
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ // register interceptor and intercept intent
+ when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(true);
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
+ .isFalse();
+
+ // unregister interceptor and launch activity
+ when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
+ .isTrue();
+ }
+
+ @Test
+ public void canActivityBeLaunched_noMatchIntentFilter_activityLaunches() {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("testing"));
+
+ GenericWindowPolicyController gwpc = createGwpc();
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ // register interceptor with different filter
+ when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
+ .isTrue();
+ verify(mIntentListenerCallback).shouldInterceptIntent(any(Intent.class));
+ }
+
+ @Test
+ public void onTopActivitychanged_null_noCallback() {
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ gwpc.onTopActivityChanged(null, 0, 0);
+ verify(mActivityListener, never())
+ .onTopActivityChanged(anyInt(), any(ComponentName.class), anyInt());
+ }
+
+ @Test
+ public void onTopActivitychanged_activityListenerCallbackObserved() {
+ int userId = 1000;
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ gwpc.onTopActivityChanged(BLOCKED_COMPONENT, 0, userId);
+ verify(mActivityListener)
+ .onTopActivityChanged(eq(DISPLAY_ID), eq(BLOCKED_COMPONENT), eq(userId));
+ }
+
+ @Test
+ public void keepActivityOnWindowFlagsChanged_noChange() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0, 0)).isTrue();
+
+ verify(mSecureWindowCallback, never()).onSecureWindowShown(DISPLAY_ID,
+ activityInfo.applicationInfo.uid);
+ verify(mActivityBlockedCallback, never()).onActivityBlocked(DISPLAY_ID, activityInfo);
+ }
+
+ @Test
+ public void keepActivityOnWindowFlagsChanged_flagSecure_isAllowedAfterTM() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, FLAG_SECURE, 0)).isTrue();
+
+ verify(mSecureWindowCallback).onSecureWindowShown(DISPLAY_ID,
+ activityInfo.applicationInfo.uid);
+ verify(mActivityBlockedCallback, never()).onActivityBlocked(DISPLAY_ID, activityInfo);
+ }
+
+ @Test
+ public void keepActivityOnWindowFlagsChanged_systemFlagHideNonSystemOverlayWindows_isAllowedAfterTM() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0,
+ SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)).isTrue();
+
+ verify(mSecureWindowCallback, never()).onSecureWindowShown(DISPLAY_ID,
+ activityInfo.applicationInfo.uid);
+ verify(mActivityBlockedCallback, never()).onActivityBlocked(DISPLAY_ID, activityInfo);
+ }
+
+ @Test
+ public void getCustomHomeComponent_noneSet() {
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ assertThat(gwpc.getCustomHomeComponent()).isNull();
+ }
+
+ @Test
+ public void getCustomHomeComponent_returnsHomeComponent() {
+ GenericWindowPolicyController gwpc = createGwpcWithCustomHomeComponent(
+ NONBLOCKED_COMPONENT);
+
+ assertThat(gwpc.getCustomHomeComponent()).isEqualTo(NONBLOCKED_COMPONENT);
+ }
+
+ private GenericWindowPolicyController createGwpc() {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ new ArraySet<>(),
+ /* crossTaskNavigationAllowedByDefault= */ true,
+ /* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ mSecureWindowCallback,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private GenericWindowPolicyController createGwpcWithCustomHomeComponent(
+ ComponentName homeComponent) {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ new ArraySet<>(),
+ /* crossTaskNavigationAllowedByDefault= */ true,
+ /* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ homeComponent);
+ }
+
+ private GenericWindowPolicyController createGwpcWithBlockedComponent(
+ ComponentName blockedComponent) {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ Collections.singleton(blockedComponent),
+ /* crossTaskNavigationAllowedByDefault= */ true,
+ /* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private GenericWindowPolicyController createGwpcWithAllowedComponent(
+ ComponentName allowedComponent) {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ false,
+ /* activityPolicyExemptions= */ Collections.singleton(allowedComponent),
+ /* crossTaskNavigationAllowedByDefault= */ true,
+ /* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private GenericWindowPolicyController createGwpcWithDisplayCategory(
+ String displayCategory) {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ new ArraySet<>(),
+ /* crossTaskNavigationAllowedByDefault= */ true,
+ /* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ Collections.singleton(displayCategory),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private GenericWindowPolicyController createGwpcWithCrossTaskNavigationBlockedFor(
+ ComponentName blockedComponent) {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ new ArraySet<>(),
+ /* crossTaskNavigationAllowedByDefault= */ true,
+ /* crossTaskNavigationExemptions= */ Collections.singleton(blockedComponent),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private GenericWindowPolicyController createGwpcWithCrossTaskNavigationAllowed(
+ ComponentName allowedComponent) {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ new ArraySet<>(),
+ /* crossTaskNavigationAllowedByDefault= */ false,
+ /* crossTaskNavigationExemptions= */ Collections.singleton(allowedComponent),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private GenericWindowPolicyController createGwpcWithPermissionComponent(
+ ComponentName permissionComponent) {
+ //TODO instert the component
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ new ArraySet<>(),
+ /* crossTaskNavigationAllowedByDefault= */ false,
+ /* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent= */ permissionComponent,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private Set<UserHandle> getCurrentUserId() {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ return new ArraySet<>(Arrays.asList(context.getUser()));
+ }
+
+ private ActivityInfo getActivityInfo(
+ String packageName, String name, boolean displayOnRemoteDevices,
+ String requiredDisplayCategory) {
+ return getActivityInfo(packageName, name, displayOnRemoteDevices, requiredDisplayCategory,
+ 0);
+ }
+
+ private ActivityInfo getActivityInfo(
+ String packageName, String name, boolean displayOnRemoteDevices,
+ String requiredDisplayCategory, int uid) {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = uid;
+
+ ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.packageName = packageName;
+ activityInfo.name = name;
+ activityInfo.flags = displayOnRemoteDevices
+ ? FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES : FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES;
+ activityInfo.applicationInfo = applicationInfo;
+ activityInfo.requiredDisplayCategory = requiredDisplayCategory;
+ return activityInfo;
+ }
+
+ private void assertActivityCanBeLaunched(GenericWindowPolicyController gwpc,
+ ActivityInfo activityInfo) {
+ assertActivityCanBeLaunched(gwpc, DISPLAY_ID, false,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ private void assertActivityCanBeLaunched(GenericWindowPolicyController gwpc, int fromDisplay,
+ boolean isNewTask, int windowingMode, ActivityInfo activityInfo) {
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay,
+ isNewTask)).isTrue();
+
+ verify(mActivityBlockedCallback, never()).onActivityBlocked(fromDisplay, activityInfo);
+ verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
+ }
+
+ private void assertActivityIsBlocked(GenericWindowPolicyController gwpc,
+ ActivityInfo activityInfo) {
+ assertActivityIsBlocked(gwpc, DISPLAY_ID, false,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ private void assertActivityIsBlocked(GenericWindowPolicyController gwpc, int fromDisplay,
+ boolean isNewTask, int windowingMode, ActivityInfo activityInfo) {
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay,
+ isNewTask)).isFalse();
+
+ verify(mActivityBlockedCallback).onActivityBlocked(fromDisplay, activityInfo);
+ verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualCameraTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualCameraTest.java
new file mode 100644
index 0000000..8f77e9b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualCameraTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.Manifest;
+import android.companion.virtual.VirtualDeviceParams;
+import android.companion.virtual.camera.IVirtualCamera;
+import android.companion.virtual.camera.IVirtualCameraSession;
+import android.companion.virtual.camera.VirtualCamera;
+import android.companion.virtual.camera.VirtualCameraConfig;
+import android.companion.virtual.camera.VirtualCameraHalConfig;
+import android.companion.virtual.camera.VirtualCameraSession;
+import android.companion.virtual.camera.VirtualCameraStreamConfig;
+import android.companion.virtual.flags.Flags;
+import android.content.ComponentName;
+import android.graphics.ImageFormat;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+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.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.AdoptShellPermissionsRule;
+import com.android.server.companion.virtual.camera.IVirtualCameraService;
+import com.android.server.companion.virtual.camera.VirtualCameraController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class VirtualCameraTest {
+
+ private static final String PKG = "com.android.virtualcamera";
+ private static final String CLS = ".VirtualCameraService";
+ public static final String CAMERA_DISPLAY_NAME = "testCamera";
+
+ private final TestableContext mContext =
+ new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
+ private FakeVirtualCameraService mFakeVirtualCameraService;
+ private VirtualCameraController mVirtualCameraController;
+
+ @Rule public final VirtualDeviceRule mVirtualDeviceRule = new VirtualDeviceRule(mContext);
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Rule
+ public AdoptShellPermissionsRule mAdoptShellPermissionsRule =
+ new AdoptShellPermissionsRule(
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ Manifest.permission.CREATE_VIRTUAL_DEVICE);
+
+ @Before
+ public void setUp() {
+ mVirtualDeviceRule.withVirtualCameraControllerSupplier(() -> mVirtualCameraController);
+ mFakeVirtualCameraService = new FakeVirtualCameraService();
+ connectFakeService();
+ mVirtualCameraController = new VirtualCameraController(mContext);
+ }
+
+ private VirtualDeviceImpl createVirtualDevice() {
+ return mVirtualDeviceRule.createVirtualDevice(new VirtualDeviceParams.Builder().build());
+ }
+
+ private void connectFakeService() {
+ mContext.addMockService(
+ ComponentName.createRelative(PKG, CLS), mFakeVirtualCameraService.asBinder());
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_VIRTUAL_CAMERA)
+ @Test
+ public void addVirtualCamera() {
+ VirtualDeviceImpl virtualDevice = createVirtualDevice();
+ VirtualCameraConfig config = createVirtualCameraConfig(null);
+ IVirtualCamera.Default camera = new IVirtualCamera.Default();
+ virtualDevice.registerVirtualCamera(camera);
+
+ assertThat(mFakeVirtualCameraService.mCameras).contains(camera);
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_VIRTUAL_CAMERA)
+ @Test
+ public void addVirtualCamera_serviceNotReady() {
+ TestableContext context =
+ new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
+ VirtualCameraController virtualCameraController = new VirtualCameraController(context);
+ mVirtualDeviceRule.withVirtualCameraControllerSupplier(() -> virtualCameraController);
+
+ VirtualDeviceImpl virtualDevice =
+ mVirtualDeviceRule.createVirtualDevice(new VirtualDeviceParams.Builder().build());
+ IVirtualCamera.Default camera = new IVirtualCamera.Default();
+ VirtualCameraConfig config = createVirtualCameraConfig(null);
+ virtualDevice.registerVirtualCamera(camera);
+ FakeVirtualCameraService fakeVirtualCameraService = new FakeVirtualCameraService();
+
+ // Only add the service after connecting the camera
+ virtualCameraController.onServiceConnected(
+ ComponentName.createRelative(PKG, CLS), fakeVirtualCameraService.asBinder());
+
+ assertThat(fakeVirtualCameraService.mCameras).contains(camera);
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_VIRTUAL_CAMERA)
+ @Test
+ public void getCameraConfiguration() {
+ VirtualDeviceImpl virtualDevice = createVirtualDevice();
+ VirtualCameraSession virtualCameraSession = new VirtualCameraSession() {};
+ VirtualCameraConfig config =
+ new VirtualCameraConfig.Builder()
+ .addStreamConfiguration(10, 10, ImageFormat.RGB_565)
+ .setDisplayName(CAMERA_DISPLAY_NAME)
+ .setCallback(
+ new HandlerExecutor(new Handler(Looper.getMainLooper())),
+ () -> virtualCameraSession)
+ .build();
+
+ VirtualCamera virtualCamera = new VirtualCamera(virtualDevice, config);
+
+ VirtualCameraConfig returnedConfig = virtualCamera.getConfig();
+ assertThat(returnedConfig).isNotNull();
+ assertThat(returnedConfig.getDisplayName()).isEqualTo(CAMERA_DISPLAY_NAME);
+ Set<VirtualCameraStreamConfig> streamConfigs = returnedConfig.getStreamConfigs();
+ assertThat(streamConfigs).hasSize(1);
+ VirtualCameraStreamConfig streamConfig =
+ streamConfigs.toArray(new VirtualCameraStreamConfig[0])[0];
+ assertThat(streamConfig.format).isEqualTo(ImageFormat.RGB_565);
+ assertThat(streamConfig.width).isEqualTo(10);
+ assertThat(streamConfig.height).isEqualTo(10);
+
+ VirtualCameraHalConfig halConfig = virtualCamera.getHalConfig();
+ assertThat(halConfig).isNotNull();
+ assertThat(halConfig.displayName).isEqualTo(CAMERA_DISPLAY_NAME);
+ assertThat(halConfig.streamConfigs).asList().hasSize(1);
+ assertThat(halConfig.streamConfigs[0].format).isEqualTo(ImageFormat.RGB_565);
+ assertThat(halConfig.streamConfigs[0].width).isEqualTo(10);
+ assertThat(halConfig.streamConfigs[0].height).isEqualTo(10);
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_VIRTUAL_CAMERA)
+ @Test
+ public void createCameraWithVirtualCameraInstance() {
+ VirtualDeviceImpl virtualDevice = createVirtualDevice();
+
+ VirtualCameraSession virtualCameraSession = new VirtualCameraSession() {};
+ VirtualCameraConfig config = createVirtualCameraConfig(virtualCameraSession);
+ VirtualCamera virtualCamera = new VirtualCamera(virtualDevice, config);
+
+ assertThat(mFakeVirtualCameraService.mCameras).contains(virtualCamera);
+ assertThat(virtualCamera.open()).isInstanceOf(IVirtualCameraSession.class);
+ }
+
+ @RequiresFlagsDisabled(Flags.FLAG_VIRTUAL_CAMERA)
+ @Test
+ public void createCameraDoesNothingWhenControllerIsNull() {
+ mVirtualDeviceRule.withVirtualCameraControllerSupplier(() -> null);
+ VirtualDeviceImpl virtualDevice = createVirtualDevice();
+ IVirtualCamera.Default camera = new IVirtualCamera.Default();
+ VirtualCameraConfig config = createVirtualCameraConfig(null);
+ virtualDevice.registerVirtualCamera(camera);
+
+ assertThat(mFakeVirtualCameraService.mCameras).doesNotContain(camera);
+ }
+
+ @NonNull
+ private static VirtualCameraConfig createVirtualCameraConfig(
+ VirtualCameraSession virtualCameraSession) {
+ return new VirtualCameraConfig.Builder()
+ .addStreamConfiguration(10, 10, ImageFormat.RGB_565)
+ .setDisplayName(CAMERA_DISPLAY_NAME)
+ .setCallback(
+ new HandlerExecutor(new Handler(Looper.getMainLooper())),
+ () -> virtualCameraSession)
+ .build();
+ }
+
+ private static class FakeVirtualCameraService extends IVirtualCameraService.Stub {
+
+ final Set<IVirtualCamera> mCameras = new HashSet<>();
+
+ @Override
+ public boolean registerCamera(IVirtualCamera camera) throws RemoteException {
+ mCameras.add(camera);
+ return true;
+ }
+
+ @Override
+ public void unregisterCamera(IVirtualCamera camera) throws RemoteException {
+ mCameras.remove(camera);
+ }
+ }
+}
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 1e6306c..61b30a0 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
@@ -24,6 +24,7 @@
import static android.content.Context.DEVICE_ID_INVALID;
import static android.content.Intent.ACTION_VIEW;
import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
+import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -70,6 +71,7 @@
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
@@ -116,6 +118,7 @@
import com.android.internal.app.BlockedAppStreamingActivity;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
+import com.android.server.companion.virtual.camera.VirtualCameraController;
import com.android.server.input.InputManagerInternal;
import com.android.server.sensors.SensorManagerInternal;
@@ -303,7 +306,7 @@
ActivityInfo activityInfo = getActivityInfo(
NONBLOCKED_APP_PACKAGE_NAME,
NONBLOCKED_APP_PACKAGE_NAME,
- /* displayOnRemoveDevices= */ true,
+ /* displayOnRemoteDevices= */ true,
targetDisplayCategory);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -314,12 +317,12 @@
private ActivityInfo getActivityInfo(
- String packageName, String name, boolean displayOnRemoveDevices,
+ String packageName, String name, boolean displayOnRemoteDevices,
String requiredDisplayCategory) {
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.packageName = packageName;
activityInfo.name = name;
- activityInfo.flags = displayOnRemoveDevices
+ activityInfo.flags = displayOnRemoteDevices
? FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES : FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES;
activityInfo.applicationInfo = mApplicationInfoMock;
activityInfo.requiredDisplayCategory = requiredDisplayCategory;
@@ -1427,7 +1430,7 @@
ActivityInfo activityInfo = getActivityInfo(
NONBLOCKED_APP_PACKAGE_NAME,
NONBLOCKED_APP_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -1448,7 +1451,7 @@
ActivityInfo activityInfo = getActivityInfo(
PERMISSION_CONTROLLER_PACKAGE_NAME,
PERMISSION_CONTROLLER_PACKAGE_NAME,
- /* displayOnRemoveDevices */ false,
+ /* displayOnRemoteDevices */ false,
/* targetDisplayCategory */ null);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -1513,7 +1516,7 @@
ActivityInfo activityInfo = getActivityInfo(
SETTINGS_PACKAGE_NAME,
SETTINGS_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -1534,7 +1537,7 @@
ActivityInfo activityInfo = getActivityInfo(
VENDING_PACKAGE_NAME,
VENDING_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -1555,7 +1558,7 @@
ActivityInfo activityInfo = getActivityInfo(
GOOGLE_DIALER_PACKAGE_NAME,
GOOGLE_DIALER_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -1576,7 +1579,7 @@
ActivityInfo activityInfo = getActivityInfo(
GOOGLE_MAPS_PACKAGE_NAME,
GOOGLE_MAPS_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -1616,6 +1619,54 @@
}
@Test
+ public void canActivityBeLaunched_permissionDialog_flagDisabled_isBlocked() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_STREAM_PERMISSIONS);
+ VirtualDeviceParams params = new VirtualDeviceParams.Builder().build();
+ mDeviceImpl.close();
+ mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params);
+ doNothing().when(mContext).startActivityAsUser(any(), any(), any());
+
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
+ DISPLAY_ID_1);
+ ComponentName permissionComponent = getPermissionDialogComponent();
+ ActivityInfo activityInfo = getActivityInfo(
+ permissionComponent.getPackageName(),
+ permissionComponent.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, null,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false))
+ .isFalse();
+
+ Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
+ activityInfo, mAssociationInfo.getDisplayName());
+ verify(mContext).startActivityAsUser(argThat(intent ->
+ intent.filterEquals(blockedAppIntent)), any(), any());
+ }
+
+ @Test
+ public void canActivityBeLaunched_permissionDialog_flagEnabled_isStreamed() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_STREAM_PERMISSIONS);
+ VirtualDeviceParams params = new VirtualDeviceParams.Builder().build();
+ mDeviceImpl.close();
+ mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params);
+
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
+ DISPLAY_ID_1);
+ ComponentName permissionComponent = getPermissionDialogComponent();
+ ActivityInfo activityInfo = getActivityInfo(
+ permissionComponent.getPackageName(),
+ permissionComponent.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, null,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false))
+ .isTrue();
+ }
+
+ @Test
public void canActivityBeLaunched_activityCanLaunch() {
Intent intent = new Intent(ACTION_VIEW, Uri.parse(TEST_SITE));
addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
@@ -1624,7 +1675,7 @@
ActivityInfo activityInfo = getActivityInfo(
NONBLOCKED_APP_PACKAGE_NAME,
NONBLOCKED_APP_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false))
@@ -1648,7 +1699,7 @@
ActivityInfo activityInfo = getActivityInfo(
NONBLOCKED_APP_PACKAGE_NAME,
NONBLOCKED_APP_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_VIEW);
@@ -1691,7 +1742,7 @@
ActivityInfo activityInfo = getActivityInfo(
NONBLOCKED_APP_PACKAGE_NAME,
NONBLOCKED_APP_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_VIEW);
@@ -1791,13 +1842,25 @@
private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid,
VirtualDeviceParams params) {
- VirtualDeviceImpl virtualDeviceImpl = new VirtualDeviceImpl(mContext,
- mAssociationInfo, mVdms, mVirtualDeviceLog, new Binder(),
- new AttributionSource(ownerUid, "com.android.virtualdevice.test", "virtualdevice"),
- virtualDeviceId,
- mInputController, mCameraAccessController,
- mPendingTrampolineCallback, mActivityListener, mSoundEffectListener,
- mRunningAppsChangedCallback, params, new DisplayManagerGlobal(mIDisplayManager));
+ VirtualDeviceImpl virtualDeviceImpl =
+ new VirtualDeviceImpl(
+ mContext,
+ mAssociationInfo,
+ mVdms,
+ mVirtualDeviceLog,
+ new Binder(),
+ new AttributionSource(
+ ownerUid, "com.android.virtualdevice.test", "virtualdevice"),
+ virtualDeviceId,
+ mInputController,
+ mCameraAccessController,
+ mPendingTrampolineCallback,
+ mActivityListener,
+ mSoundEffectListener,
+ mRunningAppsChangedCallback,
+ params,
+ new DisplayManagerGlobal(mIDisplayManager),
+ new VirtualCameraController(mContext));
mVdms.addVirtualDevice(virtualDeviceImpl);
assertThat(virtualDeviceImpl.getAssociationId()).isEqualTo(mAssociationInfo.getId());
assertThat(virtualDeviceImpl.getPersistentDeviceId())
@@ -1812,6 +1875,13 @@
NONBLOCKED_APP_PACKAGE_NAME);
}
+ private ComponentName getPermissionDialogComponent() {
+ Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
+ PackageManager packageManager = mContext.getPackageManager();
+ intent.setPackage(packageManager.getPermissionControllerPackageName());
+ return intent.resolveActivity(packageManager);
+ }
+
/** Helper class to drop permissions temporarily and restore them at the end of a test. */
static final class DropShellPermissionsTemporarily implements AutoCloseable {
DropShellPermissionsTemporarily() {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java
new file mode 100644
index 0000000..af633cc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+
+import android.app.admin.DevicePolicyManager;
+import android.companion.AssociationInfo;
+import android.companion.virtual.IVirtualDeviceActivityListener;
+import android.companion.virtual.IVirtualDeviceSoundEffectListener;
+import android.companion.virtual.VirtualDeviceParams;
+import android.companion.virtual.flags.Flags;
+import android.content.AttributionSource;
+import android.content.Context;
+import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.IDisplayManager;
+import android.net.MacAddress;
+import android.os.Binder;
+import android.testing.TestableContext;
+import android.util.ArraySet;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.LocalServices;
+import com.android.server.companion.virtual.camera.VirtualCameraController;
+import com.android.server.input.InputManagerInternal;
+import com.android.server.sensors.SensorManagerInternal;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/** Test rule to generate instances of {@link VirtualDeviceImpl}. */
+public class VirtualDeviceRule implements TestRule {
+
+ private static final int DEVICE_OWNER_UID = 50;
+ private static final int VIRTUAL_DEVICE_ID = 42;
+
+ private final Context mContext;
+ private InputController mInputController;
+ private CameraAccessController mCameraAccessController;
+ private AssociationInfo mAssociationInfo;
+ private VirtualDeviceManagerService mVdms;
+ private VirtualDeviceManagerInternal mLocalService;
+ private VirtualDeviceLog mVirtualDeviceLog;
+
+ // Mocks
+ @Mock private InputController.NativeWrapper mNativeWrapperMock;
+ @Mock private DisplayManagerInternal mDisplayManagerInternalMock;
+ @Mock private IDisplayManager mIDisplayManager;
+ @Mock private VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback;
+ @Mock private DevicePolicyManager mDevicePolicyManagerMock;
+ @Mock private InputManagerInternal mInputManagerInternalMock;
+ @Mock private SensorManagerInternal mSensorManagerInternalMock;
+ @Mock private IVirtualDeviceActivityListener mActivityListener;
+ @Mock private IVirtualDeviceSoundEffectListener mSoundEffectListener;
+ @Mock private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
+ @Mock private CameraAccessController.CameraAccessBlockedCallback mCameraAccessBlockedCallback;
+
+ // Test instance suppliers
+ private Supplier<VirtualCameraController> mVirtualCameraControllerSupplier;
+
+ /**
+ * Create a new {@link VirtualDeviceRule}
+ *
+ * @param context The context to be used with the rule.
+ */
+ public VirtualDeviceRule(@NonNull Context context) {
+ Objects.requireNonNull(context);
+ mContext = context;
+ }
+
+ /**
+ * Sets a supplier that will supply an instance of {@link VirtualCameraController}. If the
+ * supplier returns null, a new instance will be created.
+ */
+ public VirtualDeviceRule withVirtualCameraControllerSupplier(
+ Supplier<VirtualCameraController> virtualCameraControllerSupplier) {
+ mVirtualCameraControllerSupplier = virtualCameraControllerSupplier;
+ return this;
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ init(new TestableContext(mContext));
+ base.evaluate();
+ }
+ };
+ }
+
+ private void init(@NonNull TestableContext context) {
+ MockitoAnnotations.initMocks(this);
+
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+
+ doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
+ doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt());
+ doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt());
+ LocalServices.removeServiceForTest(InputManagerInternal.class);
+ LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
+
+ LocalServices.removeServiceForTest(SensorManagerInternal.class);
+ LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock);
+
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.uniqueId = "uniqueId";
+ doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt());
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+
+ context.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManagerMock);
+
+ // Allow virtual devices to be created on the looper thread for testing.
+ final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true;
+ mInputController =
+ new InputController(
+ mNativeWrapperMock,
+ InstrumentationRegistry.getInstrumentation()
+ .getContext()
+ .getMainThreadHandler(),
+ context.getSystemService(WindowManager.class),
+ threadVerifier);
+ mCameraAccessController =
+ new CameraAccessController(context, mLocalService, mCameraAccessBlockedCallback);
+
+ mAssociationInfo =
+ new AssociationInfo(
+ /* associationId= */ 1,
+ 0,
+ null,
+ null,
+ MacAddress.BROADCAST_ADDRESS,
+ "",
+ null,
+ null,
+ true,
+ false,
+ false,
+ 0,
+ 0,
+ -1);
+
+ mVdms = new VirtualDeviceManagerService(context);
+ mLocalService = mVdms.getLocalServiceInstance();
+ mVirtualDeviceLog = new VirtualDeviceLog(context);
+ }
+
+ /**
+ * Create a {@link VirtualDeviceImpl} with the required mocks
+ *
+ * @param params See {@link
+ * android.companion.virtual.VirtualDeviceManager#createVirtualDevice(int,
+ * VirtualDeviceParams)}
+ */
+ public VirtualDeviceImpl createVirtualDevice(VirtualDeviceParams params) {
+ VirtualCameraController virtualCameraController = mVirtualCameraControllerSupplier.get();
+ if (Flags.virtualCamera()) {
+ if (virtualCameraController == null) {
+ virtualCameraController = new VirtualCameraController(mContext);
+ }
+ }
+
+ VirtualDeviceImpl virtualDeviceImpl =
+ new VirtualDeviceImpl(
+ mContext,
+ mAssociationInfo,
+ mVdms,
+ mVirtualDeviceLog,
+ new Binder(),
+ new AttributionSource(
+ DEVICE_OWNER_UID,
+ "com.android.virtualdevice.test",
+ "virtualdevicerule"),
+ VIRTUAL_DEVICE_ID,
+ mInputController,
+ mCameraAccessController,
+ mPendingTrampolineCallback,
+ mActivityListener,
+ mSoundEffectListener,
+ mRunningAppsChangedCallback,
+ params,
+ new DisplayManagerGlobal(mIDisplayManager),
+ virtualCameraController);
+ mVdms.addVirtualDevice(virtualDeviceImpl);
+ return virtualDeviceImpl;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java
index 90d9452..07dd59d2 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java
@@ -32,11 +32,12 @@
import android.companion.virtual.flags.Flags;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -51,6 +52,9 @@
private static final String DEVICE_NAME = "VirtualDeviceName";
private static final String DISPLAY_NAME = "DisplayName";
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock
private IVirtualDevice mVirtualDevice;
@@ -101,9 +105,10 @@
assertThat(device.getDisplayName().toString()).isEqualTo(DISPLAY_NAME);
}
- @RequiresFlagsEnabled(Flags.FLAG_VDM_PUBLIC_APIS)
@Test
public void virtualDevice_getDisplayIds() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_VDM_PUBLIC_APIS);
+
VirtualDevice virtualDevice =
new VirtualDevice(
mVirtualDevice, VIRTUAL_DEVICE_ID, /*persistentId=*/null, /*name=*/null);
@@ -116,9 +121,10 @@
assertThat(virtualDevice.getDisplayIds()).isEqualTo(displayIds);
}
- @RequiresFlagsEnabled(Flags.FLAG_VDM_PUBLIC_APIS)
@Test
public void virtualDevice_hasCustomSensorSupport() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_VDM_PUBLIC_APIS);
+
VirtualDevice virtualDevice =
new VirtualDevice(
mVirtualDevice, VIRTUAL_DEVICE_ID, /*persistentId=*/null, /*name=*/null);
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/recoverysystem/TEST_MAPPING
index 4d1e405..e9d8b2e 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/TEST_MAPPING
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/TEST_MAPPING
@@ -7,7 +7,7 @@
"include-filter": "com.android.server.recoverysystem."
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
index 5a482fc..f221b75 100644
--- a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
@@ -484,6 +484,7 @@
new Callback());
verify(mMockStatusBar).requestAddTile(
+ eq(Binder.getCallingUid()),
eq(TEST_COMPONENT),
eq(APP_NAME),
eq(TILE_LABEL),
@@ -534,6 +535,7 @@
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
verify(mMockStatusBar).requestAddTile(
+ eq(Binder.getCallingUid()),
eq(TEST_COMPONENT),
eq(APP_NAME),
eq(TILE_LABEL),
@@ -555,6 +557,7 @@
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
verify(mMockStatusBar).requestAddTile(
+ eq(Binder.getCallingUid()),
eq(TEST_COMPONENT),
eq(APP_NAME),
eq(TILE_LABEL),
@@ -577,6 +580,7 @@
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
verify(mMockStatusBar).requestAddTile(
+ eq(Binder.getCallingUid()),
eq(TEST_COMPONENT),
eq(APP_NAME),
eq(TILE_LABEL),
@@ -599,6 +603,7 @@
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
verify(mMockStatusBar).requestAddTile(
+ eq(Binder.getCallingUid()),
eq(TEST_COMPONENT),
eq(APP_NAME),
eq(TILE_LABEL),
@@ -623,6 +628,7 @@
new Callback());
verify(mMockStatusBar, times(i + 1)).requestAddTile(
+ eq(Binder.getCallingUid()),
eq(TEST_COMPONENT),
eq(APP_NAME),
eq(TILE_LABEL),
@@ -638,6 +644,7 @@
// Only called MAX_NUM_DENIALS times
verify(mMockStatusBar, times(TileRequestTracker.MAX_NUM_DENIALS)).requestAddTile(
+ anyInt(),
any(),
any(),
any(),
@@ -658,6 +665,7 @@
new Callback());
verify(mMockStatusBar, times(i + 1)).requestAddTile(
+ eq(Binder.getCallingUid()),
eq(TEST_COMPONENT),
eq(APP_NAME),
eq(TILE_LABEL),
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 f65cb93..40ac7b1c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2855,14 +2855,14 @@
.setTask(sourceRecord.getTask()).build();
secondRecord.showStartingWindow(null /* prev */, true /* newTask */, false,
true /* startActivity */, sourceRecord);
- assertTrue(secondRecord.mAllowIconSplashScreen);
+ assertFalse(secondRecord.mSplashScreenStyleSolidColor);
secondRecord.onStartingWindowDrawn();
final ActivityRecord finalRecord = new ActivityBuilder(mAtm)
.setTask(sourceRecord.getTask()).build();
finalRecord.showStartingWindow(null /* prev */, true /* newTask */, false,
true /* startActivity */, secondRecord);
- assertFalse(finalRecord.mAllowIconSplashScreen);
+ assertTrue(finalRecord.mSplashScreenStyleSolidColor);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 25619b9..4c25a4b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -91,6 +91,7 @@
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
+import java.util.stream.Collectors;
/**
* Build/Install/Run:
@@ -563,6 +564,38 @@
}
@Test
+ public void testTasksWithCorrectOrderOfLastActiveTime() {
+ mRecentTasks.setOnlyTestVisibleRange();
+ mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_0_ID);
+
+ // Setup some tasks for the user
+ mTaskPersister.mUserTaskIdsOverride = new SparseBooleanArray();
+ mTaskPersister.mUserTaskIdsOverride.put(1, true);
+ mTaskPersister.mUserTaskIdsOverride.put(2, true);
+ mTaskPersister.mUserTaskIdsOverride.put(3, true);
+ mTaskPersister.mUserTasksOverride = new ArrayList<>();
+ mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask1").build());
+ mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask2").build());
+ mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask3").build());
+
+ // Assert no user tasks are initially loaded
+ assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).hasLength(0);
+
+ // Load tasks
+ mRecentTasks.loadUserRecentsLocked(TEST_USER_0_ID);
+ assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_0_ID);
+
+ // Sort the time descendingly so the order should be in-sync with task recency (most
+ // recent to least recent)
+ List<Task> tasksSortedByTime = mRecentTasks.getRawTasks().stream()
+ .sorted((o1, o2) -> Long.compare(o2.lastActiveTime, o1.lastActiveTime))
+ .collect(Collectors.toList());
+
+ assertTrue("Task order is not in sync with its recency",
+ mRecentTasks.getRawTasks().equals(tasksSortedByTime));
+ }
+
+ @Test
public void testOrderedIteration() {
mRecentTasks.setOnlyTestVisibleRange();
Task task1 = createTaskBuilder(".Task1").build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index bfa279d..2bf1385 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -212,7 +212,30 @@
mController.dispatchPendingEvents();
assertTaskFragmentParentInfoChangedTransaction(mTask);
- assertTaskFragmentAppearedTransaction();
+ assertTaskFragmentAppearedTransaction(false /* hasSurfaceControl */);
+ }
+
+ @Test
+ public void testOnTaskFragmentAppeared_systemOrganizer() {
+ mController.unregisterOrganizer(mIOrganizer);
+ mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+
+ // No-op when the TaskFragment is not attached.
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer, never()).onTransactionReady(any());
+
+ // Send callback when the TaskFragment is attached.
+ setupMockParent(mTaskFragment, mTask);
+
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ assertTaskFragmentParentInfoChangedTransaction(mTask);
+
+ // System organizer should receive the SurfaceControl
+ assertTaskFragmentAppearedTransaction(true /* hasSurfaceControl */);
}
@Test
@@ -1664,7 +1687,7 @@
}
/** Asserts that there will be a transaction for TaskFragment appeared. */
- private void assertTaskFragmentAppearedTransaction() {
+ private void assertTaskFragmentAppearedTransaction(boolean hasSurfaceControl) {
verify(mOrganizer).onTransactionReady(mTransactionCaptor.capture());
final TaskFragmentTransaction transaction = mTransactionCaptor.getValue();
final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
@@ -1675,6 +1698,11 @@
assertEquals(TYPE_TASK_FRAGMENT_APPEARED, change.getType());
assertEquals(mTaskFragmentInfo, change.getTaskFragmentInfo());
assertEquals(mFragmentToken, change.getTaskFragmentToken());
+ if (hasSurfaceControl) {
+ assertNotNull(change.getTaskFragmentSurfaceControl());
+ } else {
+ assertNull(change.getTaskFragmentSurfaceControl());
+ }
}
/** Asserts that there will be a transaction for TaskFragment info changed. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 6a9bb6c..5205bb0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -27,8 +27,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -124,6 +128,17 @@
}
@Test
+ public void testUpdateOrganizedTaskFragmentSurface_noSurfaceUpdateWhenOrganizedBySystem() {
+ clearInvocations(mTransaction);
+ mTaskFragment.mIsSurfaceManagedBySystemOrganizer = true;
+
+ mTaskFragment.updateOrganizedTaskFragmentSurface();
+
+ verify(mTransaction, never()).setPosition(eq(mLeash), anyFloat(), anyFloat());
+ verify(mTransaction, never()).setWindowCrop(eq(mLeash), anyInt(), anyInt());
+ }
+
+ @Test
public void testShouldStartChangeTransition_relativePositionChange() {
final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
ACTIVITY_TYPE_STANDARD);
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 29a952a..250c3a5 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -35,6 +35,8 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.FeatureFlagsImpl;
import java.util.HashMap;
import java.util.HashSet;
@@ -46,7 +48,8 @@
private static final String LOG_TAG = "TelephonyPermissions";
private static final boolean DBG = false;
-
+ /** Feature flags */
+ private static final FeatureFlags sFeatureFlag = new FeatureFlagsImpl();
/**
* Whether to disable the new device identifier access restrictions.
*/
@@ -854,7 +857,8 @@
public static boolean checkSubscriptionAssociatedWithUser(@NonNull Context context, int subId,
@NonNull UserHandle callerUserHandle, @NonNull String destAddr) {
// Skip subscription-user association check for emergency numbers
- TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+ TelephonyManager tm = (TelephonyManager) context.getSystemService(
+ Context.TELEPHONY_SERVICE);
final long token = Binder.clearCallingIdentity();
try {
if (tm != null && tm.isEmergencyNumber(destAddr)) {
@@ -876,16 +880,19 @@
* @param context Context
* @param subId subscription ID
* @param callerUserHandle caller user handle
- * @return false if user is not associated with the subscription.
+ * @return false if user is not associated with the subscription, or no record found of this
+ * subscription.
*/
public static boolean checkSubscriptionAssociatedWithUser(@NonNull Context context, int subId,
@NonNull UserHandle callerUserHandle) {
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
- // No subscription on device, return true.
+ if (!sFeatureFlag.rejectBadSubIdInteraction()
+ && !SubscriptionManager.isValidSubscriptionId(subId)) {
+ // Return true for invalid sub Id.
return true;
}
- SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class);
+ SubscriptionManager subManager = (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
final long token = Binder.clearCallingIdentity();
try {
if ((subManager != null) &&
@@ -894,8 +901,11 @@
Log.e(LOG_TAG, "User[User ID:" + callerUserHandle.getIdentifier()
+ "] is not associated with Subscription ID:" + subId);
return false;
-
}
+ } catch (IllegalArgumentException e) {
+ // Found no record of this sub Id.
+ Log.e(LOG_TAG, "Subscription[Subscription ID:" + subId + "] has no records on device");
+ return !sFeatureFlag.rejectBadSubIdInteraction();
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 9a8c965..aed8fb8 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -274,6 +274,10 @@
SubscriptionManager subscriptionManager = context.getSystemService(
SubscriptionManager.class);
+ if (!subscriptionManager.isActiveSubscriptionId(subId)) {
+ Log.e(LOG_TAG, "Tried to send message with an inactive subscription " + subId);
+ return;
+ }
UserHandle associatedUserHandle = subscriptionManager.getSubscriptionUserHandle(subId);
UserManager um = context.getSystemService(UserManager.class);
@@ -319,4 +323,4 @@
return false;
}
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 36a8fc1..a5e0638 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -52,6 +53,7 @@
import android.telephony.ims.feature.RcsFeature;
import com.android.internal.telephony.ICarrierConfigLoader;
+import com.android.internal.telephony.flags.Flags;
import com.android.telephony.Rlog;
import java.util.List;
@@ -9436,22 +9438,9 @@
* </carrier_config>
* }</pre>
* <p>
- * If this carrier config is not present, the device overlay config
- * {@code config_satellite_services_supported_by_providers} will be used. If the carrier config
- * is present, the supported services associated with the PLMNs listed in the carrier config
- * will override that of the device overlay config. The supported satellite services will be
- * identified as follows:
- * <ul>
- * <li>For each PLMN that exists only in the carrier provided satellite services, use the
- * carrier provided services as the supported services.</li>
- * <li>For each PLMN that is present only in the device provided satellite services, use the
- * device provided services as the supported services.</li>
- * <li>For each PLMN that is present in both the carrier provided and device provided satellite
- * services, use the carrier provided services as the supported services.</li>
- * </ul>
- * <p>
* This config is empty by default.
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE =
"carrier_supported_satellite_services_per_provider_bundle";
@@ -9462,9 +9451,8 @@
* satellite provider and the carrier before enabling this flag.
*
* The default value is false.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL =
"satellite_attach_supported_bool";
@@ -10430,8 +10418,12 @@
sDefaults.putAll(Iwlan.getDefaults());
sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL, false);
- sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
- new int[] {4 /* BUSY */});
+ if (Flags.doNotOverridePreciseLabel()) {
+ sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY, new int[]{});
+ } else {
+ sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
+ new int[]{4 /* BUSY */});
+ }
sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 5000);
sDefaults.putStringArray(KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY, new String[0]);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 29149b9..fa5fd87 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -4348,7 +4348,7 @@
* {code true} if there are no subscriptions on device
* else {@code false} if subscription is not associated with user.
*
- * @throws IllegalArgumentException if subscription is invalid.
+ * @throws IllegalArgumentException if subscription doesn't exist.
* @throws SecurityException if the caller doesn't have permissions required.
*
* @hide
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index a4527f12..8dc2de8 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -85,29 +85,11 @@
@Nullable private final Context mContext;
/**
- * Create a new SatelliteManager object pinned to the given subscription ID.
- * This is needed only to handle carrier specific satellite features.
- * For eg: requestSatelliteAttachEnabledForCarrier and
- * requestIsSatelliteAttachEnabledForCarrier
- *
- * @return a SatelliteManager that uses the given subId for all satellite activities.
- * @throws IllegalArgumentException if the subscription is invalid.
- * @hide
- */
- public SatelliteManager createForSubscriptionId(int subId) {
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
- throw new IllegalArgumentException("Invalid subscription ID");
- }
- return new SatelliteManager(mContext, subId);
- }
-
- /**
* Create an instance of the SatelliteManager.
*
* @param context The context the SatelliteManager belongs to.
* @hide
*/
-
public SatelliteManager(@Nullable Context context) {
this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
}
@@ -846,8 +828,8 @@
/**
* Satellite communication restricted by geolocation. This can be
* triggered based upon geofence input provided by carrier to enable or disable satellite.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1;
/** @hide */
@@ -1643,26 +1625,28 @@
* {@code true}.</li>
* </ul>
*
+ * @param subId The subscription ID of the carrier.
* @param enableSatellite {@code true} to enable the satellite and {@code false} to disable.
* @param executor The executor on which the error code listener will be called.
* @param resultListener Listener for the {@link SatelliteError} 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
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void requestSatelliteAttachEnabledForCarrier(boolean enableSatellite,
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public void requestSatelliteAttachEnabledForCarrier(int subId, boolean enableSatellite,
@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener) {
Objects.requireNonNull(executor);
Objects.requireNonNull(resultListener);
if (enableSatellite) {
- removeSatelliteAttachRestrictionForCarrier(
+ removeSatelliteAttachRestrictionForCarrier(subId,
SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener);
} else {
- addSatelliteAttachRestrictionForCarrier(
+ addSatelliteAttachRestrictionForCarrier(subId,
SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener);
}
}
@@ -1671,6 +1655,7 @@
* Request to get whether the carrier supported satellite plmn scan and attach by modem is
* enabled by user.
*
+ * @param subId The subscription ID of the carrier.
* @param executor The executor on which the callback will be called.
* @param callback The callback object to which the result will be delivered.
* If the request is successful, {@link OutcomeReceiver#onResult(Object)}
@@ -1681,16 +1666,17 @@
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void requestIsSatelliteAttachEnabledForCarrier(
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public void requestIsSatelliteAttachEnabledForCarrier(int subId,
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
- Set<Integer> restrictionReason = getSatelliteAttachRestrictionReasonsForCarrier();
+ Set<Integer> restrictionReason = getSatelliteAttachRestrictionReasonsForCarrier(subId);
executor.execute(() -> callback.onResult(
!restrictionReason.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER)));
}
@@ -1699,19 +1685,25 @@
* Add a restriction reason for disallowing carrier supported satellite plmn scan and attach
* by modem.
*
+ * @param subId The subscription ID of the carrier.
* @param reason Reason for disallowing satellite communication.
* @param executor The executor on which the error code listener will be called.
* @param resultListener Listener for the {@link SatelliteError} 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
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void addSatelliteAttachRestrictionForCarrier(
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public void addSatelliteAttachRestrictionForCarrier(int subId,
@SatelliteCommunicationRestrictionReason int reason,
@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -1722,7 +1714,7 @@
() -> resultListener.accept(result)));
}
};
- telephony.addSatelliteAttachRestrictionForCarrier(mSubId, reason, errorCallback);
+ telephony.addSatelliteAttachRestrictionForCarrier(subId, reason, errorCallback);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -1736,19 +1728,25 @@
* Remove a restriction reason for disallowing carrier supported satellite plmn scan and attach
* by modem.
*
+ * @param subId The subscription ID of the carrier.
* @param reason Reason for disallowing satellite communication.
* @param executor The executor on which the error code listener will be called.
* @param resultListener Listener for the {@link SatelliteError} 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
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void removeSatelliteAttachRestrictionForCarrier(
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public void removeSatelliteAttachRestrictionForCarrier(int subId,
@SatelliteCommunicationRestrictionReason int reason,
@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -1759,7 +1757,7 @@
() -> resultListener.accept(result)));
}
};
- telephony.removeSatelliteAttachRestrictionForCarrier(mSubId, reason, errorCallback);
+ telephony.removeSatelliteAttachRestrictionForCarrier(subId, reason, errorCallback);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -1773,34 +1771,40 @@
* Get reasons for disallowing satellite attach, as requested by
* {@link #addSatelliteAttachRestrictionForCarrier(int, Executor, Consumer)}
*
+ * @param subId The subscription ID of the carrier.
* @return Set of reasons for disallowing satellite communication.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@SatelliteCommunicationRestrictionReason
- public @NonNull Set<Integer> getSatelliteAttachRestrictionReasonsForCarrier() {
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ @NonNull public Set<Integer> getSatelliteAttachRestrictionReasonsForCarrier(int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
int[] receivedArray =
- telephony.getSatelliteAttachRestrictionReasonsForCarrier(mSubId);
+ telephony.getSatelliteAttachRestrictionReasonsForCarrier(subId);
if (receivedArray.length == 0) {
- logd("received set is empty, create empty set");
+ logd("receivedArray is empty, create empty set");
return new HashSet<>();
} else {
return Arrays.stream(receivedArray).boxed().collect(Collectors.toSet());
}
} else {
- throw new IllegalStateException("telephony service is null.");
+ throw new IllegalStateException("Telephony service is null.");
}
} catch (RemoteException ex) {
loge("getSatelliteAttachRestrictionReasonsForCarrier() RemoteException: " + ex);
ex.rethrowFromSystemServer();
}
- return null;
+ return new HashSet<>();
}
private static ITelephony getITelephony() {
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index 02661de..0fcd0d6 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -392,7 +392,11 @@
*
* @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use
* this information to determine the relevant carrier.
- * @param plmnList The list of roaming PLMN used for connecting to satellite networks.
+ * @param carrierPlmnList The list of roaming PLMN used for connecting to satellite networks
+ * supported by user subscription.
+ * @param allSatellitePlmnList Modem should use the allSatellitePlmnList to identify satellite
+ * PLMNs that are not supported by the carrier and make sure not to
+ * attach to them.
* @param resultCallback The callback to receive the error code result of the operation.
*
* Valid error codes returned:
@@ -404,8 +408,8 @@
* SatelliteError:RADIO_NOT_AVAILABLE
* SatelliteError:REQUEST_NOT_SUPPORTED
*/
- void setSatellitePlmn(int simSlot, in List<String> plmnList,
- in IIntegerConsumer resultCallback);
+ void setSatellitePlmn(int simSlot, in List<String> carrierPlmnList,
+ in List<String> allSatellitePlmnList, in IIntegerConsumer resultCallback);
/**
* Enable or disable satellite in the cellular modem associated with a carrier.
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index 6451daf..a9c09c9 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -214,12 +214,12 @@
}
@Override
- public void setSatellitePlmn(int simSlot, List<String> plmnList,
- IIntegerConsumer errorCallback)
+ public void setSatellitePlmn(int simSlot, List<String> carrierPlmnList,
+ List<String> devicePlmnList, IIntegerConsumer errorCallback)
throws RemoteException {
executeMethodAsync(
- () -> SatelliteImplBase.this
- .setSatellitePlmn(simSlot, plmnList, errorCallback),
+ () -> SatelliteImplBase.this.setSatellitePlmn(
+ simSlot, carrierPlmnList, devicePlmnList, errorCallback),
"setSatellitePlmn");
}
@@ -655,13 +655,15 @@
* SIM profile. Acquisition of satellite based system is lower priority to terrestrial
* networks. UE shall make all attempts to acquire terrestrial service prior to camping on
* satellite LTE service.
- * This method must only take effect if {@link #setSatelliteEnabledForCarrier} is {@code true},
- * and return an error otherwise.
*
* @param simLogicalSlotIndex Indicates the SIM logical slot index to which this API will be
* applied. The modem will use this information to determine the relevant carrier.
* @param errorCallback The callback to receive the error code result of the operation.
- * @param plmnList The list of roaming PLMN used for connecting to satellite networks.
+ * @param carrierPlmnList The list of roaming PLMN used for connecting to satellite networks
+ * supported by user subscription.
+ * @param allSatellitePlmnList Modem should use the allSatellitePlmnList to identify satellite
+ * PLMNs that are not supported by the carrier and make sure not to
+ * attach to them.
*
* Valid error codes returned:
* SatelliteError:NONE
@@ -672,7 +674,8 @@
* SatelliteError:RADIO_NOT_AVAILABLE
* SatelliteError:REQUEST_NOT_SUPPORTED
*/
- public void setSatellitePlmn(@NonNull int simLogicalSlotIndex, @NonNull List<String> plmnList,
+ public void setSatellitePlmn(@NonNull int simLogicalSlotIndex,
+ @NonNull List<String> carrierPlmnList, @NonNull List<String> allSatellitePlmnList,
@NonNull IIntegerConsumer errorCallback) {
// stub implementation
}
diff --git a/tests/utils/testutils/TEST_MAPPING b/tests/utils/testutils/TEST_MAPPING
index 6468d8c..52fd5a8 100644
--- a/tests/utils/testutils/TEST_MAPPING
+++ b/tests/utils/testutils/TEST_MAPPING
@@ -7,9 +7,6 @@
"exclude-annotation": "androidx.test.filters.LargeTest"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
index f7719a6..d176b5e 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
@@ -38,6 +38,10 @@
private static final boolean SKIP_METHOD_LOG = "1".equals(System.getenv(
"HOSTTEST_SKIP_METHOD_LOG"));
+ /** If true, we won't print class load log. */
+ private static final boolean SKIP_CLASS_LOG = "1".equals(System.getenv(
+ "HOSTTEST_SKIP_CLASS_LOG"));
+
/** If true, we won't perform non-stub method direct call check. */
private static final boolean SKIP_NON_STUB_METHOD_CHECK = "1".equals(System.getenv(
"HOSTTEST_SKIP_NON_STUB_METHOD_CHECK"));
@@ -57,17 +61,35 @@
}
/**
- * Called from methods with FilterPolicy.Log.
+ * Trampoline method for method-call-hook.
+ */
+ public static void callMethodCallHook(
+ Class<?> methodClass,
+ String methodName,
+ String methodDescriptor,
+ String callbackMethod
+ ) {
+ callStaticMethodByName(callbackMethod, "method call hook", methodClass,
+ methodName, methodDescriptor);
+ }
+
+ /**
+ * I can be used as
+ * {@code --default-method-call-hook
+ * com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall}.
+ *
+ * It logs every single methods called.
*/
public static void logMethodCall(
- String methodClass,
+ Class<?> methodClass,
String methodName,
String methodDescriptor
) {
if (SKIP_METHOD_LOG) {
return;
}
- logPrintStream.println("# " + methodClass + "." + methodName + methodDescriptor);
+ logPrintStream.println("# method called: " + methodClass.getCanonicalName() + "."
+ + methodName + methodDescriptor);
}
private static final StackWalker sStackWalker =
@@ -146,52 +168,81 @@
logPrintStream.println("! Class loaded: " + loadedClass.getCanonicalName()
+ " calling hook " + callbackMethod);
- // Forward the call to callbackMethod.
- final int lastPeriod = callbackMethod.lastIndexOf(".");
- final String className = callbackMethod.substring(0, lastPeriod);
- final String methodName = callbackMethod.substring(lastPeriod + 1);
+ callStaticMethodByName(callbackMethod, "class load hook", loadedClass);
+ }
- if (lastPeriod < 0 || className.isEmpty() || methodName.isEmpty()) {
+ private static void callStaticMethodByName(String classAndMethodName,
+ String description, Object... args) {
+ // Forward the call to callbackMethod.
+ final int lastPeriod = classAndMethodName.lastIndexOf(".");
+
+ if ((lastPeriod) < 0 || (lastPeriod == classAndMethodName.length() - 1)) {
throw new HostTestException(String.format(
- "Unable to find class load hook: malformed method name \"%s\"",
- callbackMethod));
+ "Unable to find %s: malformed method name \"%s\"",
+ description,
+ classAndMethodName));
}
+ final String className = classAndMethodName.substring(0, lastPeriod);
+ final String methodName = classAndMethodName.substring(lastPeriod + 1);
+
Class<?> clazz = null;
try {
clazz = Class.forName(className);
} catch (Exception e) {
throw new HostTestException(String.format(
- "Unable to find class load hook: Class %s not found", className), e);
+ "Unable to find %s: Class %s not found",
+ description,
+ className), e);
}
if (!Modifier.isPublic(clazz.getModifiers())) {
throw new HostTestException(String.format(
- "Unable to find class load hook: Class %s must be public", className));
+ "Unable to find %s: Class %s must be public",
+ description,
+ className));
+ }
+
+ Class<?>[] argTypes = new Class[args.length];
+ for (int i = 0; i < args.length; i++) {
+ argTypes[i] = args[i].getClass();
}
Method method = null;
try {
- method = clazz.getMethod(methodName, Class.class);
+ method = clazz.getMethod(methodName, argTypes);
} catch (Exception e) {
throw new HostTestException(String.format(
- "Unable to find class load hook: class %s doesn't have method %s"
- + " (method must take exactly one parameter of type Class, and public static)",
- className,
- methodName), e);
+ "Unable to find %s: class %s doesn't have method %s"
+ + " (method must take exactly one parameter of type Class,"
+ + " and public static)",
+ description, className, methodName), e);
}
if (!(Modifier.isPublic(method.getModifiers())
&& Modifier.isStatic(method.getModifiers()))) {
throw new HostTestException(String.format(
- "Unable to find class load hook: Method %s in class %s must be public static",
- methodName, className));
+ "Unable to find %s: Method %s in class %s must be public static",
+ description, methodName, className));
}
try {
- method.invoke(null, loadedClass);
+ method.invoke(null, args);
} catch (Exception e) {
throw new HostTestException(String.format(
- "Unable to invoke class load hook %s.%s",
- className,
- methodName), e);
+ "Unable to invoke %s %s.%s",
+ description, className, methodName), e);
}
}
+
+ /**
+ * I can be used as
+ * {@code --default-class-load-hook
+ * com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded}.
+ *
+ * It logs every loaded class.
+ */
+ public static void logClassLoaded(Class<?> clazz) {
+ if (SKIP_CLASS_LOG) {
+ return;
+ }
+ logPrintStream.println("# class loaded: " + clazz.getCanonicalName());
+ }
}
diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
index 828d2a3..3f87527 100644
--- a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
+++ b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
@@ -6,8 +6,10 @@
--enable-non-stub-method-check
# --no-non-stub-method-check
-# --enable-method-logging
-
+#--default-method-call-hook
+# com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+#--default-class-load-hook
+# com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
# Standard annotations.
# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`.
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 8db4b69..7531759 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -17,6 +17,7 @@
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.filters.AnnotationBasedFilter
+import com.android.hoststubgen.filters.DefaultHookInjectingFilter
import com.android.hoststubgen.filters.ClassWidePolicyPropagatingFilter
import com.android.hoststubgen.filters.ConstantFilter
import com.android.hoststubgen.filters.FilterPolicy
@@ -156,22 +157,29 @@
// This is used when a member (methods, fields, nested classes) don't get any polices
// from upper filters. e.g. when a method has no annotations, then this filter will apply
// the class-wide policy, if any. (if not, we'll fall back to the above filter.)
- val classWidePropagator = ClassWidePolicyPropagatingFilter(filter)
+ filter = ClassWidePolicyPropagatingFilter(filter)
+
+ // Inject default hooks from options.
+ filter = DefaultHookInjectingFilter(
+ options.defaultClassLoadHook,
+ options.defaultMethodCallHook,
+ filter
+ )
// Next, Java annotation based filter.
filter = AnnotationBasedFilter(
- errors,
- allClasses,
- options.stubAnnotations,
- options.keepAnnotations,
- options.stubClassAnnotations,
- options.keepClassAnnotations,
- options.throwAnnotations,
- options.removeAnnotations,
- options.substituteAnnotations,
- options.nativeSubstituteAnnotations,
- options.classLoadHookAnnotations,
- classWidePropagator
+ errors,
+ allClasses,
+ options.stubAnnotations,
+ options.keepAnnotations,
+ options.stubClassAnnotations,
+ options.keepClassAnnotations,
+ options.throwAnnotations,
+ options.removeAnnotations,
+ options.substituteAnnotations,
+ options.nativeSubstituteAnnotations,
+ options.classLoadHookAnnotations,
+ filter
)
// Next, "text based" filter, which allows to override polices without touching
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 9a54ecf..bbb7dab 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -48,6 +48,9 @@
var nativeSubstituteAnnotations: MutableSet<String> = mutableSetOf(),
var classLoadHookAnnotations: MutableSet<String> = mutableSetOf(),
+ var defaultClassLoadHook: String? = null,
+ var defaultMethodCallHook: String? = null,
+
var intersectStubJars: MutableSet<String> = mutableSetOf(),
var policyOverrideFile: String? = null,
@@ -63,8 +66,6 @@
var enablePreTrace: Boolean = false,
var enablePostTrace: Boolean = false,
- var enableMethodLogging: Boolean = false,
-
var enableNonStubMethodCallDetection: Boolean = true,
) {
companion object {
@@ -151,6 +152,12 @@
ret.classLoadHookAnnotations +=
ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--default-class-load-hook" ->
+ ret.defaultClassLoadHook = ai.nextArgRequired(arg)
+
+ "--default-method-call-hook" ->
+ ret.defaultMethodCallHook = ai.nextArgRequired(arg)
+
"--intersect-stub-jar" ->
ret.intersectStubJars += ai.nextArgRequired(arg).ensureFileExists()
@@ -167,9 +174,6 @@
"--enable-post-trace" -> ret.enablePostTrace = true
"--no-post-trace" -> ret.enablePostTrace = false
- "--enable-method-logging" -> ret.enableMethodLogging = true
- "--no-method-logging" -> ret.enableMethodLogging = false
-
"--enable-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = true
"--no-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = false
@@ -290,6 +294,8 @@
substituteAnnotations=$substituteAnnotations,
nativeSubstituteAnnotations=$nativeSubstituteAnnotations,
classLoadHookAnnotations=$classLoadHookAnnotations,
+ defaultClassLoadHook=$defaultClassLoadHook,
+ defaultMethodCallHook=$defaultMethodCallHook,
intersectStubJars=$intersectStubJars,
policyOverrideFile=$policyOverrideFile,
defaultPolicy=$defaultPolicy,
@@ -299,7 +305,6 @@
enableClassChecker=$enableClassChecker,
enablePreTrace=$enablePreTrace,
enablePostTrace=$enablePostTrace,
- enableMethodLogging=$enableMethodLogging,
enableNonStubMethodCallDetection=$enableNonStubMethodCallDetection,
}
""".trimIndent()
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
index 9fbd6d0..f75062b 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
@@ -31,3 +31,31 @@
// Remove surrounding whitespace.
return uncommented.trim()
}
+
+/**
+ * Concatenate list [a] and [b] and return it. As an optimization, it returns an input
+ * [List] as-is if the other [List] is empty, so do not modify input [List]'s.
+ */
+fun <T> addLists(a: List<T>, b: List<T>): List<T> {
+ if (a.isEmpty()) {
+ return b
+ }
+ if (b.isEmpty()) {
+ return a
+ }
+ return a + b
+}
+
+/**
+ * Add element [b] to list [a] if [b] is not null. Otherwise, just return [a].
+ * (because the method may return [a] as-is, do not modify it after passing it.)
+ */
+fun <T> addNonNullElement(a: List<T>, b: T?): List<T> {
+ if (b == null) {
+ return a
+ }
+ if (a.isEmpty()) {
+ return listOf(b)
+ }
+ return a + b
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
index 454569d..3f492e8 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
@@ -19,6 +19,7 @@
import com.android.hoststubgen.HostStubGenErrors
import com.android.hoststubgen.HostStubGenInternalException
import com.android.hoststubgen.InvalidAnnotationException
+import com.android.hoststubgen.addNonNullElement
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.findAnnotationValueAsString
import com.android.hoststubgen.asm.findAnyAnnotation
@@ -253,14 +254,14 @@
return null
}
- override fun getClassLoadHook(className: String): String? {
- classes.getClass(className).let { cn ->
+ override fun getClassLoadHooks(className: String): List<String> {
+ val e = classes.getClass(className).let { cn ->
findAnyAnnotation(classLoadHookAnnotations,
cn.visibleAnnotations, cn.invisibleAnnotations)?.let { an ->
- return getAnnotationField(an, "value")?.toHumanReadableMethodName()
+ getAnnotationField(an, "value")?.toHumanReadableMethodName()
}
}
- return null
+ return addNonNullElement(super.getClassLoadHooks(className), e)
}
private data class MethodKey(val name: String, val desc: String)
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
new file mode 100644
index 0000000..d771003
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.hoststubgen.filters
+
+import com.android.hoststubgen.addLists
+
+class DefaultHookInjectingFilter(
+ defaultClassLoadHook: String?,
+ defaultMethodCallHook: String?,
+ fallback: OutputFilter
+) : DelegatingFilter(fallback) {
+ /**
+ * Create a List containing a single element [e], if e != null. Otherwise, returns
+ * an empty list.
+ */
+ private fun toSingleList(e: String?): List<String> {
+ if (e == null) {
+ return emptyList()
+ }
+ return listOf(e)
+ }
+
+ private val defaultClassLoadHookAsList: List<String> = toSingleList(defaultClassLoadHook)
+ private val defaultMethodCallHookAsList: List<String> = toSingleList(defaultMethodCallHook)
+
+ override fun getClassLoadHooks(className: String): List<String> {
+ return addLists(super.getClassLoadHooks(className), defaultClassLoadHookAsList)
+ }
+
+ override fun getMethodCallHooks(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): List<String> {
+ return addLists(
+ super.getMethodCallHooks(className, methodName, descriptor),
+ defaultMethodCallHookAsList,
+ )
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
index f0763c4..45f61c5 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
@@ -66,7 +66,15 @@
return fallback.getNativeSubstitutionClass(className)
}
- override fun getClassLoadHook(className: String): String? {
- return fallback.getClassLoadHook(className)
+ override fun getClassLoadHooks(className: String): List<String> {
+ return fallback.getClassLoadHooks(className)
+ }
+
+ override fun getMethodCallHooks(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): List<String> {
+ return fallback.getMethodCallHooks(className, methodName, descriptor)
}
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
index f3551d4..5659a35 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
@@ -16,6 +16,7 @@
package com.android.hoststubgen.filters
import com.android.hoststubgen.UnknownApiException
+import com.android.hoststubgen.addNonNullElement
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.toHumanReadableClassName
import com.android.hoststubgen.asm.toHumanReadableMethodName
@@ -127,9 +128,9 @@
mNativeSubstitutionClasses[getClassKey(from)] = to.toHumanReadableClassName()
}
- override fun getClassLoadHook(className: String): String? {
- return mClassLoadHooks[getClassKey(className)]
- ?: super.getClassLoadHook(className)
+ override fun getClassLoadHooks(className: String): List<String> {
+ return addNonNullElement(super.getClassLoadHooks(className),
+ mClassLoadHooks[getClassKey(className)])
}
fun setClassLoadHook(className: String, methodName: String) {
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
index 392ee4b..3df16ff 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
@@ -71,11 +71,22 @@
}
/**
- * Return a "class load hook" method name for a given class.
+ * Return a "class load hook" class name for a given class.
*
* (which corresponds to @HostSideTestClassLoadHook of the standard annotations.)
*/
- open fun getClassLoadHook(className: String): String? {
- return null
+ open fun getClassLoadHooks(className: String): List<String> {
+ return emptyList()
+ }
+
+ /**
+ * Return the "method call hook" class name.
+ *
+ * The class has to have a function with the following signature:
+ * `public static void onMethodCalled(Class<?> clazz, String name, String descriptor)`.
+ */
+ open fun getMethodCallHooks(className: String, methodName: String, descriptor: String):
+ List<String> {
+ return emptyList()
}
}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index ac06886..57b6689 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -45,7 +45,7 @@
return policy.needsInImpl
}
- private var classLoadHookMethod: String? = null
+ private var classLoadHooks: List<String> = emptyList()
override fun visit(
version: Int,
@@ -57,22 +57,22 @@
) {
super.visit(version, access, name, signature, superName, interfaces)
- classLoadHookMethod = filter.getClassLoadHook(currentClassName)
+ classLoadHooks = filter.getClassLoadHooks(currentClassName)
// classLoadHookMethod is non-null, then we need to inject code to call it
// in the class initializer.
// If the target class already has a class initializer, then we need to inject code to it.
// Otherwise, we need to create one.
- classLoadHookMethod?.let { callback ->
- log.d(" ClassLoadHook: $callback")
+ if (classLoadHooks.isNotEmpty()) {
+ log.d(" ClassLoadHooks: $classLoadHooks")
if (!classes.hasClassInitializer(currentClassName)) {
- injectClassLoadHook(callback)
+ injectClassLoadHook()
}
}
}
- private fun injectClassLoadHook(callback: String) {
+ private fun injectClassLoadHook() {
writeRawMembers {
// Create a class initializer to call onClassLoaded().
// Each class can only have at most one class initializer, but the base class
@@ -87,7 +87,7 @@
// Method prologue
mv.visitCode()
- writeClassLoadHookCall(mv)
+ writeClassLoadHookCalls(mv)
mv.visitInsn(Opcodes.RETURN)
// Method epilogue
@@ -97,21 +97,23 @@
}
}
- private fun writeClassLoadHookCall(mv: MethodVisitor) {
- // First argument: the class type.
- mv.visitLdcInsn(Type.getType("L" + currentClassName + ";"))
+ private fun writeClassLoadHookCalls(mv: MethodVisitor) {
+ classLoadHooks.forEach { classLoadHook ->
+ // First argument: the class type.
+ mv.visitLdcInsn(Type.getType("L" + currentClassName + ";"))
- // Second argument: method name
- mv.visitLdcInsn(classLoadHookMethod)
+ // Second argument: method name
+ mv.visitLdcInsn(classLoadHook)
- // Call HostTestUtils.onClassLoaded().
- mv.visitMethodInsn(
- Opcodes.INVOKESTATIC,
- HostTestUtils.CLASS_INTERNAL_NAME,
- "onClassLoaded",
- "(Ljava/lang/Class;Ljava/lang/String;)V",
- false
- )
+ // Call HostTestUtils.onClassLoaded().
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ HostTestUtils.CLASS_INTERNAL_NAME,
+ "onClassLoaded",
+ "(Ljava/lang/Class;Ljava/lang/String;)V",
+ false
+ )
+ }
}
override fun updateAccessFlags(
@@ -138,20 +140,22 @@
var innerVisitor = superVisitor
// If method logging is enabled, inject call to the logging method.
- if (options.enableMethodLogging) {
- innerVisitor = LogInjectingMethodAdapter(
- access,
- name,
- descriptor,
- signature,
- exceptions,
- innerVisitor,
- )
+ val methodCallHooks = filter.getMethodCallHooks(currentClassName, name, descriptor)
+ if (methodCallHooks.isNotEmpty()) {
+ innerVisitor = MethodCallHookInjectingAdapter(
+ access,
+ name,
+ descriptor,
+ signature,
+ exceptions,
+ innerVisitor,
+ methodCallHooks,
+ )
}
// If this class already has a class initializer and a class load hook is needed, then
// we inject code.
- if (classLoadHookMethod != null &&
+ if (classLoadHooks.isNotEmpty() &&
name == CLASS_INITIALIZER_NAME &&
descriptor == CLASS_INITIALIZER_DESC) {
innerVisitor = ClassLoadHookInjectingMethodAdapter(
@@ -283,29 +287,37 @@
}
/**
- * A method adapter that injects a call to HostTestUtils.logMethodCall() to every method.
+ * Inject calls to the method call hooks.
*
* Note, when the target method is a constructor, it may contain calls to `super(...)` or
* `this(...)`. The logging code will be injected *before* such calls.
*/
- private inner class LogInjectingMethodAdapter(
+ private inner class MethodCallHookInjectingAdapter(
access: Int,
val name: String,
val descriptor: String,
signature: String?,
exceptions: Array<String>?,
- next: MethodVisitor?
+ next: MethodVisitor?,
+ val hooks: List<String>,
) : MethodVisitor(OPCODE_VERSION, next) {
override fun visitCode() {
super.visitCode()
- visitLdcInsn(currentClassName)
- visitLdcInsn(name)
- visitLdcInsn(descriptor)
- visitMethodInsn(Opcodes.INVOKESTATIC,
+
+ hooks.forEach { hook ->
+ mv.visitLdcInsn(Type.getType("L" + currentClassName + ";"))
+ visitLdcInsn(name)
+ visitLdcInsn(descriptor)
+ visitLdcInsn(hook)
+
+ visitMethodInsn(
+ Opcodes.INVOKESTATIC,
HostTestUtils.CLASS_INTERNAL_NAME,
- "logMethodCall",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
- false)
+ "callMethodCallHook",
+ "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
+ false
+ )
+ }
}
}
@@ -323,7 +335,7 @@
override fun visitCode() {
super.visitCode()
- writeClassLoadHookCall(this)
+ writeClassLoadHookCalls(this)
}
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
index 8c76a61..05d6a43 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
@@ -53,6 +53,46 @@
],
}
+// Same as "hoststubgen-test-tiny-framework-host", but with more options, to test more hoststubgen
+// features.
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-ext",
+ defaults: ["hoststubgen-command-defaults"],
+ cmd: hoststubgen_common_options +
+ "--in-jar $(location :hoststubgen-test-tiny-framework) " +
+ "--policy-override-file $(location policy-override-tiny-framework.txt) " +
+
+ // More options.
+ "--default-method-call-hook com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall " +
+ "--default-class-load-hook com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded ",
+ srcs: [
+ ":hoststubgen-test-tiny-framework",
+ "policy-override-tiny-framework.txt",
+ ],
+}
+
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-ext-stub",
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":hoststubgen-test-tiny-framework-host-ext{host_stub.jar}",
+ ],
+ out: [
+ "host_stub.jar",
+ ],
+}
+
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-ext-impl",
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":hoststubgen-test-tiny-framework-host-ext{host_impl.jar}",
+ ],
+ out: [
+ "host_impl.jar",
+ ],
+}
+
// Compile the test jar, using 2 rules.
// 1. Build the test against the stub.
java_library_host {
@@ -123,6 +163,30 @@
visibility: ["//visibility:private"],
}
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-ext-stub-dump",
+ defaults: ["hoststubgen-jar-dump-defaults"],
+ srcs: [
+ ":hoststubgen-test-tiny-framework-host-ext-stub",
+ ],
+ out: [
+ "12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-ext-impl-dump",
+ defaults: ["hoststubgen-jar-dump-defaults"],
+ srcs: [
+ ":hoststubgen-test-tiny-framework-host-ext-impl",
+ ],
+ out: [
+ "13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt",
+ ],
+ visibility: ["//visibility:private"],
+}
+
// Run it with `atest`. Compare the dump of the jar files to the golden output.
python_test_host {
name: "tiny-framework-dump-test",
@@ -136,6 +200,8 @@
"hoststubgen-test-tiny-framework-host-stub-dump",
"hoststubgen-test-tiny-framework-host-impl-dump",
"hoststubgen-test-tiny-framework-orig-dump",
+ "hoststubgen-test-tiny-framework-host-ext-stub-dump",
+ "hoststubgen-test-tiny-framework-host-ext-impl-dump",
],
test_suites: ["general-tests"],
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
index 4d58869..639fb16 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
@@ -112,7 +112,7 @@
if (( $three_way )) ; then
echo "# Running 3-way diff with meld..."
- run meld ${files[*]} &
+ run meld ${files[0]} ${files[1]} ${files[2]} &
fi
if (( $two_way )) ; then
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
new file mode 100644
index 0000000..43ceec4
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -0,0 +1,837 @@
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 4
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int getOneStub();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+SourceFile: "TinyFrameworkCallerCheck.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 3, attributes: 5
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int getOne_withCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int getOne_noCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+SourceFile: "TinyFrameworkCallerCheck.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
+ Compiled from "TinyFrameworkClassAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 5, attributes: 3
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+SourceFile: "TinyFrameworkClassAnnotations.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
+ Compiled from "TinyFrameworkClassClassWideAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 3, methods: 8, attributes: 3
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int remove;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public void toBeRemoved(java.lang.String);
+ descriptor: (Ljava/lang/String;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
+ Compiled from "TinyFrameworkClassLoadHook.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 3, attributes: 3
+ public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
+ descriptor: Ljava/util/Set;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>;
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static void onClassLoaded(java.lang.Class<?>);
+ descriptor: (Ljava/lang/Class;)V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // (Ljava/lang/Class<*>;)V
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+SourceFile: "TinyFrameworkClassLoadHook.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
+ Compiled from "TinyFrameworkClassWithInitializer.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 3
+ public static boolean sInitialized;
+ descriptor: Z
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+SourceFile: "TinyFrameworkClassWithInitializer.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
+ Compiled from "TinyFrameworkExceptionTester.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int testException();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+SourceFile: "TinyFrameworkExceptionTester.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
+ Compiled from "TinyFrameworkForTextPolicy.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 5, attributes: 2
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+SourceFile: "TinyFrameworkForTextPolicy.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
+ Compiled from "TinyFrameworkNative.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 5, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static native int nativeAddTwo(int);
+ descriptor: (I)I
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+
+ public static int nativeAddTwo_should_be_like_this(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static native long nativeLongPlus(long, long);
+ descriptor: (JJ)J
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+
+ public static long nativeLongPlus_should_be_like_this(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=4, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+SourceFile: "TinyFrameworkNative.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+ x: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
+ value="TinyFrameworkNative_host"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 4
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 1, attributes: 5
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 5
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+}
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ interfaces: 0, fields: 0, methods: 1, attributes: 4
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 4, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
new file mode 100644
index 0000000..874789e
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -0,0 +1,2348 @@
+## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class
+ Compiled from "HostSideTestClassLoadHook.java"
+public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestClassLoadHook
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 2, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestClassLoadHook
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public abstract java.lang.String value();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+}
+SourceFile: "HostSideTestClassLoadHook.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestKeep.class
+ Compiled from "HostSideTestKeep.java"
+public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestKeep
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestKeep
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "HostSideTestKeep.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class
+ Compiled from "HostSideTestNativeSubstitutionClass.java"
+public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestNativeSubstitutionClass
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 2, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestNativeSubstitutionClass
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public abstract java.lang.String value();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+}
+SourceFile: "HostSideTestNativeSubstitutionClass.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestRemove.class
+ Compiled from "HostSideTestRemove.java"
+public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestRemove
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestRemove
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "HostSideTestRemove.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestStub.class
+ Compiled from "HostSideTestStub.java"
+public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestStub
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestStub
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "HostSideTestStub.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestSubstitute.class
+ Compiled from "HostSideTestSubstitute.java"
+public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestSubstitute
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 2, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestSubstitute
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public abstract java.lang.String suffix();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+}
+SourceFile: "HostSideTestSubstitute.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.METHOD]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestThrow.class
+ Compiled from "HostSideTestThrow.java"
+public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestThrow
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestThrow
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "HostSideTestThrow.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class
+ Compiled from "HostSideTestWholeClassKeep.java"
+public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestWholeClassKeep
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestWholeClassKeep
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "HostSideTestWholeClassKeep.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class
+ Compiled from "HostSideTestWholeClassStub.java"
+public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestWholeClassStub
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestWholeClassStub
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "HostSideTestWholeClassStub.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 4, attributes: 4
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl;
+
+ public static int getOneKeep();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ x: ldc #x // String getOneKeep
+ x: ldc #x // String ()I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ x: ldc #x // String getOneKeep
+ x: ldc #x // String ()I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_1
+ x: ireturn
+ LineNumberTable:
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ public static int getOneStub();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ x: ldc #x // String getOneStub
+ x: ldc #x // String ()I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_1
+ x: ireturn
+ LineNumberTable:
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+SourceFile: "TinyFrameworkCallerCheck.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 4, attributes: 5
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck;
+
+ public static int getOne_withCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ x: ldc #x // String getOne_withCheck
+ x: ldc #x // String ()I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
+ x: ireturn
+ LineNumberTable:
+
+ public static int getOne_noCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ x: ldc #x // String getOne_noCheck
+ x: ldc #x // String ()I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
+ x: ireturn
+ LineNumberTable:
+}
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+SourceFile: "TinyFrameworkCallerCheck.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
+ Compiled from "TinyFrameworkClassAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 3
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String addOne
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 11 6 1 value I
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 26 4 1 value I
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String addTwo
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 11 4 1 value I
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String nativeAddThree
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 value I
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestThrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String visibleButUsesUnsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+SourceFile: "TinyFrameworkClassAnnotations.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
+ Compiled from "TinyFrameworkClassClassWideAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 3, methods: 9, attributes: 3
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int remove;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String addOne
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 11 6 1 value I
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 11 4 1 value I
+
+ public void toBeRemoved(java.lang.String);
+ descriptor: (Ljava/lang/String;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String toBeRemoved
+ x: ldc #x // String (Ljava/lang/String;)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 11 8 1 foo Ljava/lang/String;
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String addTwo
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 11 4 1 value I
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String nativeAddThree
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 value I
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String visibleButUsesUnsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+}
+SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
+ Compiled from "TinyFrameworkClassLoadHook.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 3, attributes: 3
+ public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
+ descriptor: Ljava/util/Set;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>;
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook;
+
+ public static void onClassLoaded(java.lang.Class<?>);
+ descriptor: (Ljava/lang/Class;)V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ x: ldc #x // String onClassLoaded
+ x: ldc #x // String (Ljava/lang/Class;)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: aload_0
+ x: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+ x: pop
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 11 0 clazz Ljava/lang/Class;
+ LocalVariableTypeTable:
+ Start Length Slot Name Signature
+ 11 11 0 clazz Ljava/lang/Class<*>;
+ Signature: #x // (Ljava/lang/Class<*>;)V
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: new #x // class java/util/HashSet
+ x: dup
+ x: invokespecial #x // Method java/util/HashSet."<init>":()V
+ x: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: return
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkClassLoadHook.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
+ Compiled from "TinyFrameworkClassWithInitializer.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 3
+ public static boolean sInitialized;
+ descriptor: Z
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer;
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: iconst_1
+ x: putstatic #x // Field sInitialized:Z
+ x: return
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkClassWithInitializer.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
+ Compiled from "TinyFrameworkExceptionTester.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 3, attributes: 3
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester;
+
+ public static int testException();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+ x: ldc #x // String testException
+ x: ldc #x // String ()I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class java/lang/IllegalStateException
+ x: dup
+ x: ldc #x // String Inner exception
+ x: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ x: astore_0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Outer exception
+ x: aload_0
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+ x: athrow
+ Exception table:
+ from to target type
+ 11 21 21 Class java/lang/Exception
+ StackMapTable: number_of_entries = 1
+ frame_type = 85 /* same_locals_1_stack_item */
+ stack = [ class java/lang/Exception ]
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 22 11 0 e Ljava/lang/Exception;
+}
+SourceFile: "TinyFrameworkExceptionTester.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
+ Compiled from "TinyFrameworkForTextPolicy.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 2
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String addOne
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 11 6 1 value I
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 26 4 1 value I
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String addTwo
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 11 4 1 value I
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String nativeAddThree
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 value I
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String visibleButUsesUnsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+}
+SourceFile: "TinyFrameworkForTextPolicy.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
+ Compiled from "TinyFrameworkNative.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 6, attributes: 3
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+
+ public static int nativeAddTwo(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ x: ireturn
+
+ public static int nativeAddTwo_should_be_like_this(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String nativeAddTwo_should_be_like_this
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 arg I
+
+ public static long nativeLongPlus(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=4, args_size=2
+ x: lload_0
+ x: lload_2
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ x: lreturn
+
+ public static long nativeLongPlus_should_be_like_this(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=4, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String nativeLongPlus_should_be_like_this
+ x: ldc #x // String (JJ)J
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: lload_0
+ x: lload_2
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ x: lreturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 arg1 J
+ 11 6 2 arg2 J
+}
+SourceFile: "TinyFrameworkNative.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+ x: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
+ value="TinyFrameworkNative_host"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class
+ Compiled from "TinyFrameworkNative_host.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 4, attributes: 3
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host;
+
+ public static int nativeAddTwo(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeAddTwo
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeAddTwo
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_0
+ x: iconst_2
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 4 0 arg I
+
+ public static long nativeLongPlus(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=4, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeLongPlus
+ x: ldc #x // String (JJ)J
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeLongPlus
+ x: ldc #x // String (JJ)J
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: lload_0
+ x: lload_2
+ x: ladd
+ x: lreturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 4 0 arg1 J
+ 26 4 2 arg2 J
+}
+SourceFile: "TinyFrameworkNative_host.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 1, methods: 4, attributes: 6
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0000)
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String <init>
+ x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+ 11 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 4, attributes: 6
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2();
+ descriptor: ()V
+ flags: (0x0000)
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 1, methods: 4, attributes: 6
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0000)
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String <init>
+ x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+ 11 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 4, attributes: 6
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4();
+ descriptor: ()V
+ flags: (0x0000)
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ x: ldc #x // String <init>
+ x: ldc #x // String (I)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field value:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass;
+ 11 10 1 x I
+}
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 2, attributes: 5
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ x: ldc #x // String <init>
+ x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_5
+ x: putfield #x // Field value:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass;
+ 11 15 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+}
+InnerClasses:
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 4, attributes: 6
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1();
+ descriptor: ()V
+ flags: (0x0000)
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+}
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 3, attributes: 5
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: bipush 6
+ x: putfield #x // Field value:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass;
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ x: ldc #x // String getSupplier_static
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+}
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ interfaces: 0, fields: 0, methods: 2, attributes: 4
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ x: ldc #x // String <init>
+ x: ldc #x // String (I)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: iload_1
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass;
+ 11 6 1 x I
+}
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 4, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 17 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ x: ldc #x // String getSupplier
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 9 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ x: ldc #x // String getSupplier_static
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh
index 6bc0ddb..7600942 100755
--- a/tools/hoststubgen/scripts/run-all-tests.sh
+++ b/tools/hoststubgen/scripts/run-all-tests.sh
@@ -34,6 +34,7 @@
run ./hoststubgen/test-framework/run-test-without-atest.sh
run ./hoststubgen/test-tiny-framework/run-test-manually.sh
+run atest tiny-framework-dump-test
run ./scripts/build-framework-hostside-jars-and-extract.sh
# This script is already broken on goog/master
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
index cbbf91b4..758de4d 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
@@ -131,7 +131,7 @@
priority = 6,
severity = Severity.ERROR,
implementation = Implementation(
- EnforcePermissionDetector::class.java,
+ EnforcePermissionHelperDetector::class.java,
EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
)
)