Merge "fix(#Magnification): fix WindowMagnificationAnimationControllerTest test case fails on cf_x86_64 devices"
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 7c8dd92..9a0053f 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -380,7 +380,7 @@
":non-updatable-current.txt",
],
defaults: ["android-non-updatable_from_text_defaults"],
- dep_api_srcs: "android_stubs_current.from-text",
+ full_api_surface_stub: "android_stubs_current.from-text",
}
java_api_library {
@@ -391,7 +391,7 @@
":non-updatable-system-current.txt",
],
defaults: ["android-non-updatable_from_text_defaults"],
- dep_api_srcs: "android_system_stubs_current.from-text",
+ full_api_surface_stub: "android_system_stubs_current.from-text",
}
java_api_library {
@@ -403,7 +403,7 @@
":non-updatable-test-current.txt",
],
defaults: ["android-non-updatable_from_text_defaults"],
- dep_api_srcs: "android_test_stubs_current.from-text",
+ full_api_surface_stub: "android_test_stubs_current.from-text",
}
java_api_library {
@@ -415,7 +415,7 @@
":non-updatable-module-lib-current.txt",
],
defaults: ["android-non-updatable_from_text_defaults"],
- dep_api_srcs: "android_module_lib_stubs_current_full.from-text",
+ full_api_surface_stub: "android_module_lib_stubs_current_full.from-text",
}
java_defaults {
diff --git a/core/api/current.txt b/core/api/current.txt
index db6dc5d..c2bc5a2 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -54946,6 +54946,7 @@
public class AnimationUtils {
ctor public AnimationUtils();
method public static long currentAnimationTimeMillis();
+ method public static long getExpectedPresentationTimeMillis();
method 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;
@@ -55780,7 +55781,7 @@
field public static final String SERVICE_INTERFACE = "android.view.InputMethod";
field public static final String SERVICE_META_DATA = "android.view.im";
field public static final int SHOW_EXPLICIT = 1; // 0x1
- field public static final int SHOW_FORCED = 2; // 0x2
+ field @Deprecated public static final int SHOW_FORCED = 2; // 0x2
}
public static interface InputMethod.SessionCallback {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 070822e..cf24f20 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -15221,7 +15221,7 @@
method @NonNull public android.telephony.data.DataCallResponse.Builder setGatewayAddresses(@NonNull java.util.List<java.net.InetAddress>);
method @NonNull public android.telephony.data.DataCallResponse.Builder setHandoverFailureMode(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setId(int);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setInterfaceName(@NonNull String);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setInterfaceName(@Nullable String);
method @NonNull public android.telephony.data.DataCallResponse.Builder setLinkStatus(int);
method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setMtu(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index bac3f4a..7c3d8fb 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1061,6 +1061,7 @@
method public boolean isMain();
method public boolean isManagedProfile();
method @Deprecated public boolean isPrimary();
+ method public boolean isPrivateProfile();
method public boolean isProfile();
method public boolean isQuietModeEnabled();
method public boolean isRestricted();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 0bdf0a0..02aeac7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3156,13 +3156,17 @@
/**
* Called by the system when the device configuration changes while your
- * activity is running. Note that this will <em>only</em> be called if
- * you have selected configurations you would like to handle with the
+ * activity is running. Note that this will only be called if you have
+ * selected configurations you would like to handle with the
* {@link android.R.attr#configChanges} attribute in your manifest. If
* any configuration change occurs that is not selected to be reported
* by that attribute, then instead of reporting it the system will stop
* and restart the activity (to have it launched with the new
- * configuration).
+ * configuration). The only exception is if a size-based configuration
+ * is not large enough to be considered significant, in which case the
+ * system will not recreate the activity and will instead call this
+ * method. For details on this see the documentation on
+ * <a href="{@docRoot}guide/topics/resources/runtime-changes.html">size-based config change</a>.
*
* <p>At the time that this function has been called, your Resources
* object will have been updated to return resource values matching the
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index be0dc18..33b8b03 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -14787,12 +14787,12 @@
}
/**
- * Called by the system to find out whether the current user's IME was set by the device/profile
- * owner or the user.
+ * Returns true if the current user's IME was set by an admin.
*
- * @return {@code true} if the user's IME was set by the device or profile owner, {@code false}
- * otherwise.
- * @throws SecurityException if the caller is not the device owner/profile owner.
+ * <p>Requires the caller to be the system server, a device owner or profile owner, or a holder
+ * of the QUERY_ADMIN_POLICY permission.
+ *
+ * @throws SecurityException if the caller is not authorized
*
* @hide
*/
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 3d76b28..d7195a7 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -65,7 +65,7 @@
* thread of your app.
*
* <p>Note on threading: the state inside of this class is not itself
- * thread-safe, however you can use it from any thread if you properly
+ * thread-safe. However, you can use it from any thread if you make
* sure that you do not have races. Typically this means you will hand
* the entire object to another thread, which will be solely responsible
* for setting any results and finally calling {@link #finish()}.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ced6401..7c9ccba 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -795,6 +795,11 @@
void setSplashScreenTheme(String packageName, String themeName, int userId);
+ int getUserMinAspectRatio(String packageName, int userId);
+
+ @EnforcePermission("INSTALL_PACKAGES")
+ void setUserMinAspectRatio(String packageName, int userId, int aspectRatio);
+
List<String> getMimeGroup(String packageName, String group);
boolean isAutoRevokeWhitelisted(String packageName);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4bc1eeb..2948bd9 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2351,6 +2351,64 @@
*/
public static final int INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST = -130;
+ /**
+ * App minimum aspect ratio set by the user which will override app-defined aspect ratio.
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "USER_MIN_ASPECT_RATIO_" }, value = {
+ USER_MIN_ASPECT_RATIO_UNSET,
+ USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
+ USER_MIN_ASPECT_RATIO_DISPLAY_SIZE,
+ USER_MIN_ASPECT_RATIO_4_3,
+ USER_MIN_ASPECT_RATIO_16_9,
+ USER_MIN_ASPECT_RATIO_3_2,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UserMinAspectRatio {}
+
+ /**
+ * No aspect ratio override has been set by user.
+ *
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_UNSET = 0;
+
+ /**
+ * Aspect ratio override code: user forces app to split screen aspect ratio. This is adjusted to
+ * half of the screen without the split screen divider.
+ *
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_SPLIT_SCREEN = 1;
+
+ /**
+ * Aspect ratio override code: user forces app to the aspect ratio of the device display size.
+ * This will be the portrait aspect ratio of the device if the app is portrait or the landscape
+ * aspect ratio of the device if the app is landscape.
+ *
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_DISPLAY_SIZE = 2;
+
+ /**
+ * Aspect ratio override code: user forces app to 4:3 min aspect ratio
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_4_3 = 3;
+
+ /**
+ * Aspect ratio override code: user forces app to 16:9 min aspect ratio
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_16_9 = 4;
+
+ /**
+ * Aspect ratio override code: user forces app to 3:2 min aspect ratio
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_3_2 = 5;
+
/** @hide */
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
DELETE_KEEP_DATA,
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 7f42c1a..9f4459d 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -390,6 +390,10 @@
return UserManager.isUserTypeCommunalProfile(userType);
}
+ public boolean isPrivateProfile() {
+ return UserManager.isUserTypePrivateProfile(userType);
+ }
+
@UnsupportedAppUsage
public boolean isEnabled() {
return (flags & FLAG_DISABLED) != FLAG_DISABLED;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 76efce5..022f3c4 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1762,6 +1762,24 @@
* 123,1,critical,0.8,default;123,1,moderate,0.6,id_2;456,2,moderate,0.9,critical,0.7
*/
String KEY_BRIGHTNESS_THROTTLING_DATA = "brightness_throttling_data";
+
+ /**
+ * Key for new power controller feature flag. If enabled new DisplayPowerController will
+ * be used.
+ * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)}
+ * with {@link android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER} as the namespace.
+ * @hide
+ */
+ String KEY_NEW_POWER_CONTROLLER = "use_newly_structured_display_power_controller";
+
+ /**
+ * Key for normal brightness mode controller feature flag.
+ * It enables NormalBrightnessModeController.
+ * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)}
+ * with {@link android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER} as the namespace.
+ * @hide
+ */
+ String KEY_USE_NORMAL_BRIGHTNESS_MODE_CONTROLLER = "use_normal_brightness_mode_controller";
}
/**
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 9e97216..c111138 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -26,8 +26,6 @@
import android.annotation.SystemApi;
import android.annotation.UserIdInt;
import android.app.Activity;
-import android.app.ActivityThread;
-import android.app.OnActivityPausedListener;
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -1473,17 +1471,11 @@
if (activity == null || intent == null) {
throw new NullPointerException();
}
- if (!activity.isResumed()) {
- throw new IllegalStateException("Foreground dispatch can only be enabled " +
- "when your activity is resumed");
- }
try {
TechListParcel parcel = null;
if (techLists != null && techLists.length > 0) {
parcel = new TechListParcel(techLists);
}
- ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,
- mForegroundDispatchListener);
sService.setForegroundDispatch(intent, filters, parcel);
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
@@ -1511,25 +1503,8 @@
throw new UnsupportedOperationException();
}
}
- ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity,
- mForegroundDispatchListener);
- disableForegroundDispatchInternal(activity, false);
- }
-
- OnActivityPausedListener mForegroundDispatchListener = new OnActivityPausedListener() {
- @Override
- public void onPaused(Activity activity) {
- disableForegroundDispatchInternal(activity, true);
- }
- };
-
- void disableForegroundDispatchInternal(Activity activity, boolean force) {
try {
sService.setForegroundDispatch(null, null, null);
- if (!force && !activity.isResumed()) {
- throw new IllegalStateException("You must disable foreground dispatching " +
- "while your activity is still resumed");
- }
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
}
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index e6bdfe1..7664bad 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -112,9 +112,17 @@
private static final int ANGLE_GL_DRIVER_ALL_ANGLE_OFF = 0;
// Values for ANGLE_GL_DRIVER_SELECTION_VALUES
- private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default";
- private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle";
- private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native";
+ private enum AngleDriverChoice {
+ DEFAULT("default"),
+ ANGLE("angle"),
+ NATIVE("native");
+
+ public final String choice;
+
+ AngleDriverChoice(String choice) {
+ this.choice = choice;
+ }
+ }
private static final String PROPERTY_RO_ANGLE_SUPPORTED = "ro.gfx.angle.supported";
@@ -195,15 +203,16 @@
}
/**
- * Query to determine if ANGLE should be used
+ * Query to determine the ANGLE driver choice.
*/
- private boolean shouldUseAngle(Context context, Bundle coreSettings, String packageName) {
+ private AngleDriverChoice queryAngleChoice(Context context, Bundle coreSettings,
+ String packageName) {
if (TextUtils.isEmpty(packageName)) {
Log.v(TAG, "No package name specified; use the system driver");
- return false;
+ return AngleDriverChoice.DEFAULT;
}
- return shouldUseAngleInternal(context, coreSettings, packageName);
+ return queryAngleChoiceInternal(context, coreSettings, packageName);
}
private int getVulkanVersion(PackageManager pm) {
@@ -424,10 +433,11 @@
* forces a choice;
* 3) Use ANGLE if isAngleEnabledByGameMode() returns true;
*/
- private boolean shouldUseAngleInternal(Context context, Bundle bundle, String packageName) {
+ private AngleDriverChoice queryAngleChoiceInternal(Context context, Bundle bundle,
+ String packageName) {
// Make sure we have a good package name
if (TextUtils.isEmpty(packageName)) {
- return false;
+ return AngleDriverChoice.DEFAULT;
}
// Check the semi-global switch (i.e. once system has booted enough) for whether ANGLE
@@ -442,7 +452,7 @@
}
if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) {
Log.v(TAG, "Turn on ANGLE for all applications.");
- return true;
+ return AngleDriverChoice.ANGLE;
}
// Get the per-application settings lists
@@ -465,7 +475,7 @@
+ optInPackages.size() + ", "
+ "number of values: "
+ optInValues.size());
- return mEnabledByGameMode;
+ return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT;
}
// See if this application is listed in the per-application settings list
@@ -473,7 +483,7 @@
if (pkgIndex < 0) {
Log.v(TAG, packageName + " is not listed in per-application setting");
- return mEnabledByGameMode;
+ return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT;
}
mAngleOptInIndex = pkgIndex;
@@ -483,14 +493,14 @@
Log.v(TAG,
"ANGLE Developer option for '" + packageName + "' "
+ "set to: '" + optInValue + "'");
- if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) {
- return true;
- } else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) {
- return false;
+ if (optInValue.equals(AngleDriverChoice.ANGLE.choice)) {
+ return AngleDriverChoice.ANGLE;
+ } else if (optInValue.equals(AngleDriverChoice.NATIVE.choice)) {
+ return AngleDriverChoice.NATIVE;
} else {
// The user either chose default or an invalid value; go with the default driver or what
// the game mode indicates
- return mEnabledByGameMode;
+ return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT;
}
}
@@ -558,7 +568,13 @@
private boolean setupAngle(Context context, Bundle bundle, PackageManager packageManager,
String packageName) {
- if (!shouldUseAngle(context, bundle, packageName)) {
+ final AngleDriverChoice angleDriverChoice = queryAngleChoice(context, bundle, packageName);
+ if (angleDriverChoice == AngleDriverChoice.DEFAULT) {
+ return false;
+ }
+
+ if (queryAngleChoice(context, bundle, packageName) == AngleDriverChoice.NATIVE) {
+ nativeSetAngleInfo("", true, packageName, null);
return false;
}
@@ -627,10 +643,10 @@
Log.d(TAG, "ANGLE package libs: " + paths);
}
- // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name,
- // and features to use.
+ // If we make it to here, ANGLE apk will be used. Call nativeSetAngleInfo() with the
+ // application package name and ANGLE features to use.
final String[] features = getAngleEglFeatures(context, bundle);
- setAngleInfo(paths, false, packageName, features);
+ nativeSetAngleInfo(paths, false, packageName, features);
return true;
}
@@ -652,10 +668,10 @@
return false;
}
- // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name,
- // and features to use.
+ // If we make it to here, system ANGLE will be used. Call nativeSetAngleInfo() with
+ // the application package name and ANGLE features to use.
final String[] features = getAngleEglFeatures(context, bundle);
- setAngleInfo("", true, packageName, features);
+ nativeSetAngleInfo("system", false, packageName, features);
return true;
}
@@ -936,8 +952,8 @@
private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries);
private static native void setGpuStats(String driverPackageName, String driverVersionName,
long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion);
- private static native void setAngleInfo(String path, boolean useSystemAngle, String packageName,
- String[] features);
+ private static native void nativeSetAngleInfo(String path, boolean useNativeDriver,
+ String packageName, String[] features);
private static native boolean setInjectLayersPrSetDumpable();
private static native void nativeToggleAngleAsSystemDriver(boolean enabled);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 84dc79b..ba1f979 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -159,6 +159,13 @@
@SystemApi
public static final String USER_TYPE_PROFILE_CLONE = "android.os.usertype.profile.CLONE";
+
+ /**
+ * User type representing a private profile.
+ * @hide
+ */
+ public static final String USER_TYPE_PROFILE_PRIVATE = "android.os.usertype.profile.PRIVATE";
+
/**
* User type representing a generic profile for testing purposes. Only on debuggable builds.
* @hide
@@ -2858,6 +2865,16 @@
}
/**
+ * Returns whether the user type is a
+ * {@link UserManager#USER_TYPE_PROFILE_PRIVATE private profile}.
+ *
+ * @hide
+ */
+ public static boolean isUserTypePrivateProfile(@Nullable String userType) {
+ return USER_TYPE_PROFILE_PRIVATE.equals(userType);
+ }
+
+ /**
* @hide
* @deprecated Use {@link #isRestrictedProfile()}
*/
@@ -3146,6 +3163,24 @@
}
/**
+ * Checks if the context user is a private profile.
+ *
+ * @return whether the context user is a private profile.
+ *
+ * @see android.os.UserManager#USER_TYPE_PROFILE_PRIVATE
+ * @hide
+ */
+ @UserHandleAware(
+ requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
+ @SuppressAutoDoc
+ public boolean isPrivateProfile() {
+ return isUserTypePrivateProfile(getProfileType());
+ }
+
+ /**
* Checks if the context user is an ephemeral user.
*
* @return whether the context user is an ephemeral user.
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 511c974..518a549 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -743,7 +743,7 @@
* @param startMillis the start time in UTC milliseconds
* @param endMillis the end time in UTC milliseconds
* @param flags a bit mask of options
- * @param timeZone the time zone to compute the string in. Use null for local
+ * @param timeZone the id of the time zone to compute the string in. Use null for local
* or if the FORMAT_UTC flag is being used.
*
* @return the formatter with the formatted date/time range appended to the string buffer.
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 0abad6c..c1eacb5 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -841,7 +841,6 @@
return mLastDispatchedState;
}
- @VisibleForTesting
public boolean onStateChanged(InsetsState state) {
boolean stateChanged = false;
if (!CAPTION_ON_SHELL) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1ecfd74..565ed61 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8662,15 +8662,43 @@
}
/**
- * Dispatches an {@link AccessibilityEvent} to the {@link View} first and then
- * to its children for adding their text content to the event. Note that the
- * event text is populated in a separate dispatch path since we add to the
+ * Dispatches an {@link AccessibilityEvent} to the {@link View} to add the text content of the
+ * view and its children.
+ * <p>
+ * <b>Note:</b> This method should only be used with event.setText().
+ * Avoid mutating other event state in this method. In general, put UI metadata in the node for
+ * services to easily query.
+ * <ul>
+ * <li> If you are modifying other event properties, you may be eliminating semantics
+ * accessibility services may want. Instead, send a separate event using
+ * {@link #sendAccessibilityEvent(int)} and override
+ * {@link #onInitializeAccessibilityEvent(AccessibilityEvent)}.
+ * </li>
+ * <li>If you are checking for type {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}
+ * to generate window/title announcements, you may be causing disruptive announcements
+ * (or making no announcements at all). Instead, follow the practices described in
+ * {@link View#announceForAccessibility(CharSequence)}. <b>Note:</b> this does not suggest
+ * calling announceForAccessibility(), but using the suggestions listed in its
+ * documentation.
+ * </li>
+ * <li>If you are making changes based on the state of accessibility, such as checking for
+ * an event type to trigger a UI update, while well-intentioned, you are creating brittle,
+ * less well-maintained code that works for some users but not others. Instead, leverage
+ * existing code for equitable experiences and less technical debt. See
+ * {@link AccessibilityManager#isEnabled()} for an example.
+ * </li>
+ * </ul>
+ * <p>
+ * Note that the event text is populated in a separate dispatch path
+ * ({@link #onPopulateAccessibilityEvent(AccessibilityEvent)}) since we add to the
* event not only the text of the source but also the text of all its descendants.
+ * <p>
* A typical implementation will call
- * {@link #onPopulateAccessibilityEvent(AccessibilityEvent)} on the this view
+ * {@link #onPopulateAccessibilityEvent(AccessibilityEvent)} on this view
* and then call the {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)}
- * on each child. Override this method if custom population of the event text
- * content is required.
+ * on each child or the first child that is visible. Override this method if custom population
+ * of the event text content is required.
+ *
* <p>
* If an {@link AccessibilityDelegate} has been specified via calling
* {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
@@ -8714,9 +8742,12 @@
/**
* Called from {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)}
* giving a chance to this View to populate the accessibility event with its
- * text content. While this method is free to modify event
- * attributes other than text content, doing so should normally be performed in
- * {@link #onInitializeAccessibilityEvent(AccessibilityEvent)}.
+ * text content.
+ * <p>
+ * <b>Note:</b> This method should only be used with event.setText().
+ * Avoid mutating other event state in this method. Instead, follow the practices described in
+ * {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)}. In general, put UI
+ * metadata in the node for services to easily query, than in transient events.
* <p>
* Example: Adding formatted date string to an accessibility event in addition
* to the text added by the super implementation:
@@ -11256,26 +11287,36 @@
}
/**
- * Sets the id of a view before which this one is visited in accessibility traversal.
- * A screen-reader must visit the content of this view before the content of the one
- * it precedes. For example, if view B is set to be before view A, then a screen-reader
- * will traverse the entire content of B before traversing the entire content of A,
- * regardles of what traversal strategy it is using.
+ * Sets the id of a view that screen readers are requested to visit after this view.
+ *
* <p>
- * Views that do not have specified before/after relationships are traversed in order
- * determined by the screen-reader.
- * </p>
+ *
+ * For example, if view B should be visited before view A, with
+ * B.setAccessibilityTraversalBefore(A), this requests that screen readers visit and traverse
+ * view B before visiting view A.
+ *
* <p>
- * Setting that this view is before a view that is not important for accessibility
- * or if this view is not important for accessibility will have no effect as the
- * screen-reader is not aware of unimportant views.
- * </p>
+ * <b>Note:</b> Views are visited in the order determined by the screen reader. Avoid
+ * explicitly manipulating focus order, as this may result in inconsistent user
+ * experiences between apps. Instead, use other semantics, such as restructuring the view
+ * hierarchy layout, to communicate order.
+ *
+ * <p>
+ * Setting this view to be after a view that is not important for accessibility,
+ * or if this view is not important for accessibility, means this method will have no effect if
+ * the service is not aware of unimportant views.
+ *
+ * <p>
+ * To avoid a risk of loops, set clear relationships between views. For example, if focus order
+ * should be B -> A, and B.setAccessibilityTraversalBefore(A), then also call
+ * A.setAccessibilityTraversalAfter(B).
*
* @param beforeId The id of a view this one precedes in accessibility traversal.
*
* @attr ref android.R.styleable#View_accessibilityTraversalBefore
*
* @see #setImportantForAccessibility(int)
+ * @see #setAccessibilityTraversalAfter(int)
*/
@RemotableViewMethod
public void setAccessibilityTraversalBefore(@IdRes int beforeId) {
@@ -11302,26 +11343,34 @@
}
/**
- * Sets the id of a view after which this one is visited in accessibility traversal.
- * A screen-reader must visit the content of the other view before the content of this
- * one. For example, if view B is set to be after view A, then a screen-reader
- * will traverse the entire content of A before traversing the entire content of B,
- * regardles of what traversal strategy it is using.
- * <p>
- * Views that do not have specified before/after relationships are traversed in order
- * determined by the screen-reader.
- * </p>
- * <p>
- * Setting that this view is after a view that is not important for accessibility
- * or if this view is not important for accessibility will have no effect as the
- * screen-reader is not aware of unimportant views.
- * </p>
+ * Sets the id of a view that screen readers are requested to visit before this view.
*
- * @param afterId The id of a view this one succedees in accessibility traversal.
+ * <p>
+ * For example, if view B should be visited after A, with B.setAccessibilityTraversalAfter(A),
+ * then this requests that screen readers visit and traverse view A before visiting view B.
+ *
+ * <p>
+ * <b>Note:</b> Views are visited in the order determined by the screen reader. Avoid
+ * explicitly manipulating focus order, as this may result in inconsistent user
+ * experiences between apps. Instead, use other semantics, such as restructuring the view
+ * hierarchy layout, to communicate order.
+ *
+ * <p>
+ * Setting this view to be after a view that is not important for accessibility,
+ * or if this view is not important for accessibility, means this method will have no effect if
+ * the service is not aware of unimportant views.
+ *
+ * <p>
+ * To avoid a risk of loops, set clear relationships between views. For example, if focus order
+ * should be B -> A, and B.setAccessibilityTraversalBefore(A), then also call
+ * A.setAccessibilityTraversalAfter(B).
+ *
+ * @param afterId The id of a view this one succeeds in accessibility traversal.
*
* @attr ref android.R.styleable#View_accessibilityTraversalAfter
*
* @see #setImportantForAccessibility(int)
+ * @see #setAccessibilityTraversalBefore(int)
*/
@RemotableViewMethod
public void setAccessibilityTraversalAfter(@IdRes int afterId) {
@@ -14619,19 +14668,31 @@
* to the view's content description or text, or to the content descriptions
* or text of the view's children (where applicable).
* <p>
- * For example, in a login screen with a TextView that displays an "incorrect
- * password" notification, that view should be marked as a live region with
- * mode {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
+ * To indicate that the user should be notified of changes, use
+ * {@link #ACCESSIBILITY_LIVE_REGION_POLITE}. Announcements from this region are queued and
+ * do not disrupt ongoing speech.
+ * <p>
+ * For example, selecting an option in a dropdown menu may update a panel below with the updated
+ * content. This panel may be marked as a live region with
+ * {@link #ACCESSIBILITY_LIVE_REGION_POLITE} to notify users of the change.
+ * <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>
* To disable change notifications for this view, use
* {@link #ACCESSIBILITY_LIVE_REGION_NONE}. This is the default live region
* mode for most views.
* <p>
- * To indicate that the user should be notified of changes, use
- * {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
- * <p>
* If the view's changes should interrupt ongoing speech and notify the user
- * immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}.
+ * immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}. This may result in disruptive
+ * announcements from an accessibility service, so it should generally be used only to convey
+ * information that is time-sensitive or critical for use of the application. Examples may
+ * include an incoming call or an emergency alert.
* <p>
* <b>Note:</b> Use {@link androidx.core.view.ViewCompat#setAccessibilityLiveRegion(View, int)}
* for backwards-compatibility. </aside>
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index f31a43f..8ba8b8c 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -30,6 +30,7 @@
import android.content.res.XmlResourceParser;
import android.os.SystemClock;
import android.util.AttributeSet;
+import android.util.TimeUtils;
import android.util.Xml;
import android.view.InflateException;
@@ -156,6 +157,18 @@
}
/**
+ * The expected presentation time of a frame in the {@link SystemClock#uptimeMillis()}.
+ * Developers should prefer using this method over {@link #currentAnimationTimeMillis()}
+ * because it offers a more accurate time for the calculating animation progress.
+ *
+ * @return the expected presentation time of a frame in the
+ * {@link SystemClock#uptimeMillis()} time base.
+ */
+ public static long getExpectedPresentationTimeMillis() {
+ return getExpectedPresentationTimeNanos() / TimeUtils.NANOS_PER_MS;
+ }
+
+ /**
* Loads an {@link Animation} object from a resource
*
* @param context Application context used to access resources
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 92380ed..6340388 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -282,7 +282,11 @@
* Flag for {@link #showSoftInput}: this show has been forced to
* happen by the user. If set, the input method should remain visible
* until deliberated dismissed by the user in its UI.
+ *
+ * @deprecated {@link InputMethodManager#SHOW_FORCED} is deprecated and
+ * should no longer be used by apps. IMEs likewise should no longer react to this flag.
*/
+ @Deprecated
public static final int SHOW_FORCED = 0x00002;
/**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 3f308e6..fb94d49 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2018,7 +2018,7 @@
*
* @deprecated Use {@link #showSoftInput} without this flag instead. Using this flag can lead
* to the soft input remaining visible even when the calling application is closed. The
- * use of this flag can make the soft input remains visible globally. Starting in
+ * use of this flag can make the soft input remain visible globally. Starting in
* {@link Build.VERSION_CODES#TIRAMISU Android T}, this flag only has an effect while the
* caller is currently focused.
*/
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index aa9225b..e9d7b9b 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -1118,7 +1118,7 @@
@InputConnection.CursorUpdateFilter int cursorUpdateFilter, int imeDisplayId) {
final InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
- Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
+ Log.w(TAG, "requestCursorUpdates on inactive InputConnection");
return false;
}
if (mParentInputMethodManager.mRequestCursorUpdateDisplayIdCheck.get()
diff --git a/core/java/android/window/WindowMetricsController.java b/core/java/android/window/WindowMetricsController.java
index 954f686..2858f0a 100644
--- a/core/java/android/window/WindowMetricsController.java
+++ b/core/java/android/window/WindowMetricsController.java
@@ -145,13 +145,13 @@
for (int i = 0; i < possibleDisplayInfos.size(); i++) {
currentDisplayInfo = possibleDisplayInfos.get(i);
- // Calculate max bounds for this rotation and state.
- Rect maxBounds = new Rect(0, 0, currentDisplayInfo.logicalWidth,
- currentDisplayInfo.logicalHeight);
+ // Calculate max bounds for natural rotation and state.
+ Rect maxBounds = new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
+ currentDisplayInfo.getNaturalHeight());
- // Calculate insets for the rotated max bounds.
+ // Calculate insets for the natural max bounds.
final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0;
- // Initialize insets based upon display rotation. Note any window-provided insets
+ // Initialize insets based on Surface.ROTATION_0. Note any window-provided insets
// will not be set.
windowInsets = getWindowInsetsFromServerForDisplay(
currentDisplayInfo.displayId, null /* token */,
diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java
index f2ae973..611da3c 100644
--- a/core/java/android/window/WindowProviderService.java
+++ b/core/java/android/window/WindowProviderService.java
@@ -34,6 +34,7 @@
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.IBinder;
+import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
@@ -52,6 +53,8 @@
@UiContext
public abstract class WindowProviderService extends Service implements WindowProvider {
+ private static final String TAG = WindowProviderService.class.getSimpleName();
+
private final Bundle mOptions;
private final WindowTokenClient mWindowToken = new WindowTokenClient();
private final WindowContextController mController = new WindowContextController(mWindowToken);
@@ -194,8 +197,16 @@
public final Context createServiceBaseContext(ActivityThread mainThread,
LoadedApk packageInfo) {
final Context context = super.createServiceBaseContext(mainThread, packageInfo);
- final Display display = context.getSystemService(DisplayManager.class)
- .getDisplay(getInitialDisplayId());
+ final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+ final int initialDisplayId = getInitialDisplayId();
+ Display display = displayManager.getDisplay(initialDisplayId);
+ // Fallback to use the default display if the initial display to start WindowProviderService
+ // is detached.
+ if (display == null) {
+ Log.e(TAG, "Display with id " + initialDisplayId + " not found, falling back to "
+ + "DEFAULT_DISPLAY");
+ display = displayManager.getDisplay(DEFAULT_DISPLAY);
+ }
return context.createTokenContext(mWindowToken, display);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 8024a63..c19265a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -379,7 +379,6 @@
"libbinary_parse",
"libdng_sdk",
"libft2",
- "libhostgraphics",
"libhwui",
"libimage_type_recognition",
"libjpeg",
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index afc3cbd..8fc30d1 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -50,7 +50,7 @@
appPackageNameChars.c_str(), vulkanVersion);
}
-void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useSystemAngle,
+void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useNativeDriver,
jstring packageName, jobjectArray featuresObj) {
ScopedUtfChars pathChars(env, path);
ScopedUtfChars packageNameChars(env, packageName);
@@ -73,7 +73,7 @@
}
}
- android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useSystemAngle,
+ android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useNativeDriver,
packageNameChars.c_str(), features);
}
@@ -118,7 +118,7 @@
reinterpret_cast<void*>(setGpuStats_native)},
{"setInjectLayersPrSetDumpable", "()Z",
reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native)},
- {"setAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V",
+ {"nativeSetAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V",
reinterpret_cast<void*>(setAngleInfo_native)},
{"setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V",
reinterpret_cast<void*>(setLayerPaths_native)},
diff --git a/core/res/res/drawable-watch/global_actions_item_grey_background_shape.xml b/core/res/res/drawable-watch/global_actions_item_grey_background_shape.xml
index f2df319..3ac9ffba 100644
--- a/core/res/res/drawable-watch/global_actions_item_grey_background_shape.xml
+++ b/core/res/res/drawable-watch/global_actions_item_grey_background_shape.xml
@@ -18,5 +18,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="26dp"/>
- <solid android:color="@color/wear_material_grey_900"/>
+ <solid android:color="?attr/colorSurface"/>
</shape>
\ No newline at end of file
diff --git a/core/res/res/drawable-watch/global_actions_item_red_background_shape.xml b/core/res/res/drawable-watch/global_actions_item_red_background_shape.xml
index 4f23700..b85e01d 100644
--- a/core/res/res/drawable-watch/global_actions_item_red_background_shape.xml
+++ b/core/res/res/drawable-watch/global_actions_item_red_background_shape.xml
@@ -18,5 +18,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="26dp"/>
- <solid android:color="@color/wear_material_red_mid"/>
+ <solid android:color="?attr/colorError"/>
</shape>
\ No newline at end of file
diff --git a/core/res/res/layout-watch/global_actions_item.xml b/core/res/res/layout-watch/global_actions_item.xml
index f964a4a..021c9ab 100644
--- a/core/res/res/layout-watch/global_actions_item.xml
+++ b/core/res/res/layout-watch/global_actions_item.xml
@@ -36,7 +36,7 @@
android:textSize="15sp"
android:letterSpacing="0.013"
android:fadingEdgeLength="12dp"
- android:textColor="@android:color/white"
+ android:textColor="?attr/textColorPrimary"
android:layout_weight="1"
android:fontFamily="google-sans-text-medium"
android:layout_width="wrap_content"
diff --git a/core/res/res/values-watch/colors.xml b/core/res/res/values-watch/colors.xml
deleted file mode 100644
index 6d908be..0000000
--- a/core/res/res/values-watch/colors.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources>
- <!-- Wear Material standard colors -->
- <color name="wear_material_red_mid">#CC5D58</color>
- <color name="wear_material_grey_900">#202124</color>
-</resources>
\ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 55122ce..d80cfa3 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3030,15 +3030,15 @@
representation this attribute can be used for providing such. -->
<attr name="contentDescription" format="string" localization="suggested" />
- <!-- Sets the id of a view before which this one is visited in accessibility traversal.
- A screen-reader must visit the content of this view before the content of the one
- it precedes.
+ <!-- Sets the id of a view that screen readers are requested to visit after this view.
+ Requests that a screen-reader visits the content of this view before the content of the
+ one it precedes. This does nothing if either view is not important for accessibility.
{@see android.view.View#setAccessibilityTraversalBefore(int)} -->
<attr name="accessibilityTraversalBefore" format="integer" />
- <!-- Sets the id of a view after which this one is visited in accessibility traversal.
- A screen-reader must visit the content of the other view before the content of
- this one.
+ <!-- Sets the id of a view that screen readers are requested to visit before this view.
+ Requests that a screen-reader visits the content of the other view before the content
+ of this one. This does nothing if either view is not important for accessibility.
{@see android.view.View#setAccessibilityTraversalAfter(int)} -->
<attr name="accessibilityTraversalAfter" format="integer" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index f28da1f..3b099e8 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -40,7 +40,7 @@
<shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
<!-- Argentina: 5 digits, known short codes listed -->
- <shortcode country="ar" pattern="\\d{5}" free="11711|28291|44077" />
+ <shortcode country="ar" pattern="\\d{5}" free="11711|28291|44077|78887" />
<!-- Armenia: 3-4 digits, emergency numbers 10[123] -->
<shortcode country="am" pattern="\\d{3,4}" premium="11[2456]1|3024" free="10[123]" />
@@ -162,7 +162,7 @@
<shortcode country="jp" pattern="\\d{1,5}" free="8083" />
<!-- Kenya: 5 digits, known premium codes listed -->
- <shortcode country="ke" pattern="\\d{5}" free="21725|21562|40520|23342" />
+ <shortcode country="ke" pattern="\\d{5}" free="21725|21562|40520|23342|40023" />
<!-- Kyrgyzstan: 4 digits, known premium codes listed -->
<shortcode country="kg" pattern="\\d{4}" premium="415[2367]|444[69]" />
@@ -208,7 +208,7 @@
<shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|176|2141|3067|3068|3110|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" />
<!-- Peru: 4-5 digits (not confirmed), known premium codes listed -->
- <shortcode country="pe" pattern="\\d{4,5}" free="9963|40777" />
+ <shortcode country="pe" pattern="\\d{4,5}" free="9963|40778" />
<!-- Philippines -->
<shortcode country="ph" pattern="\\d{1,5}" free="2147|5495|5496" />
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index b9d3756..a4c655c8c 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -782,10 +782,13 @@
@Nullable
public static Bitmap wrapHardwareBuffer(@NonNull HardwareBuffer hardwareBuffer,
@Nullable ColorSpace colorSpace) {
- if ((hardwareBuffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
+ final long usage = hardwareBuffer.getUsage();
+ if ((usage & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
throw new IllegalArgumentException("usage flags must contain USAGE_GPU_SAMPLED_IMAGE.");
}
- int format = hardwareBuffer.getFormat();
+ if ((usage & HardwareBuffer.USAGE_PROTECTED_CONTENT) != 0) {
+ throw new IllegalArgumentException("Bitmap is not compatible with protected buffers");
+ }
if (colorSpace == null) {
colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 6d7f761..73eb62a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -339,19 +339,36 @@
}
// This means an expand happened before enter-pip finished and we are now "merging" a
// no-op transition that happens to match our exit-pip.
+ // Or that the keyguard is up and preventing the transition from applying, in which case we
+ // want to manually reset pip. (b/283783868)
boolean cancelled = false;
if (mPipAnimationController.getCurrentAnimator() != null) {
mPipAnimationController.getCurrentAnimator().cancel();
+ mPipAnimationController.resetAnimatorState();
cancelled = true;
}
+
// Unset exitTransition AFTER cancel so that finishResize knows we are merging.
mExitTransition = null;
- if (!cancelled || aborted) return;
+ if (!cancelled) return;
final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo();
if (taskInfo != null) {
- startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(),
- mPipBoundsState.getBounds(), mPipBoundsState.getBounds(),
- new Rect(mExitDestinationBounds), Surface.ROTATION_0, null /* startT */);
+ if (aborted) {
+ // keyguard case - the transition got aborted, so we want to reset state and
+ // windowing mode before reapplying the resize transaction
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_LEAVE_PIP);
+ mPipOrganizer.onExitPipFinished(taskInfo);
+
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mPipOrganizer.applyWindowingModeChangeOnExit(wct, TRANSITION_DIRECTION_LEAVE_PIP);
+ wct.setBounds(taskInfo.token, null);
+ mPipOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_LEAVE_PIP, false);
+ } else {
+ // merge case
+ startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(),
+ mPipBoundsState.getBounds(), mPipBoundsState.getBounds(),
+ new Rect(mExitDestinationBounds), Surface.ROTATION_0, null /* startT */);
+ }
}
mExitDestinationBounds.setEmpty();
mCurrentPipTaskToken = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java
index 5f6b3fe..fc0b876 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java
@@ -77,6 +77,19 @@
return mActionType;
}
+ static String getActionTypeString(@ActionType int actionType) {
+ switch (actionType) {
+ case ACTION_FULLSCREEN: return "ACTION_FULLSCREEN";
+ case ACTION_CLOSE: return "ACTION_CLOSE";
+ case ACTION_MOVE: return "ACTION_MOVE";
+ case ACTION_EXPAND_COLLAPSE: return "ACTION_EXPAND_COLLAPSE";
+ case ACTION_CUSTOM: return "ACTION_CUSTOM";
+ case ACTION_CUSTOM_CLOSE: return "ACTION_CUSTOM_CLOSE";
+ default:
+ return "UNDEFINED";
+ }
+ }
+
abstract void populateButton(@NonNull TvWindowMenuActionButton button, Handler mainHandler);
abstract PendingIntent getPendingIntent();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
index 3b44f10..11c2665 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
@@ -56,8 +56,10 @@
private final List<Listener> mListeners = new ArrayList<>();
private final TvPipAction.SystemActionsHandler mSystemActionsHandler;
- private final List<TvPipAction> mActionsList;
+ private final List<TvPipAction> mActionsList = new ArrayList<>();
+ private final TvPipSystemAction mFullscreenAction;
private final TvPipSystemAction mDefaultCloseAction;
+ private final TvPipSystemAction mMoveAction;
private final TvPipSystemAction mExpandCollapseAction;
private final List<RemoteAction> mMediaActions = new ArrayList<>();
@@ -67,26 +69,27 @@
TvPipAction.SystemActionsHandler systemActionsHandler) {
mSystemActionsHandler = systemActionsHandler;
- mActionsList = new ArrayList<>();
- mActionsList.add(new TvPipSystemAction(ACTION_FULLSCREEN, R.string.pip_fullscreen,
+ mFullscreenAction = new TvPipSystemAction(ACTION_FULLSCREEN, R.string.pip_fullscreen,
R.drawable.pip_ic_fullscreen_white, ACTION_TO_FULLSCREEN, context,
- mSystemActionsHandler));
-
+ mSystemActionsHandler);
mDefaultCloseAction = new TvPipSystemAction(ACTION_CLOSE, R.string.pip_close,
R.drawable.pip_ic_close_white, ACTION_CLOSE_PIP, context, mSystemActionsHandler);
- mActionsList.add(mDefaultCloseAction);
-
- mActionsList.add(new TvPipSystemAction(ACTION_MOVE, R.string.pip_move,
- R.drawable.pip_ic_move_white, ACTION_MOVE_PIP, context, mSystemActionsHandler));
-
+ mMoveAction = new TvPipSystemAction(ACTION_MOVE, R.string.pip_move,
+ R.drawable.pip_ic_move_white, ACTION_MOVE_PIP, context, mSystemActionsHandler);
mExpandCollapseAction = new TvPipSystemAction(ACTION_EXPAND_COLLAPSE, R.string.pip_collapse,
R.drawable.pip_ic_collapse, ACTION_TOGGLE_EXPANDED_PIP, context,
mSystemActionsHandler);
- mActionsList.add(mExpandCollapseAction);
+ initActions();
pipMediaController.addActionListener(this::onMediaActionsChanged);
}
+ private void initActions() {
+ mActionsList.add(mFullscreenAction);
+ mActionsList.add(mDefaultCloseAction);
+ mActionsList.add(mMoveAction);
+ }
+
@Override
public void executeAction(@TvPipAction.ActionType int actionType) {
if (mSystemActionsHandler != null) {
@@ -199,6 +202,14 @@
}
}
+ void reset() {
+ mActionsList.clear();
+ mMediaActions.clear();
+ mAppActions.clear();
+
+ initActions();
+ }
+
List<TvPipAction> getActionsList() {
return mActionsList;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 02eeb2a..2482acf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -478,6 +478,7 @@
mActionBroadcastReceiver.unregister();
mTvPipMenuController.closeMenu();
+ mTvPipActionsProvider.reset();
mTvPipBoundsState.resetTvPipState();
mTvPipBoundsController.reset();
setState(STATE_NO_PIP);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index c964df1..c2f15f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.startingsurface;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.graphics.Color.WHITE;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -77,6 +78,13 @@
@NonNull Runnable clearWindowHandler) {
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
final int taskId = runningTaskInfo.taskId;
+
+ // if we're in PIP we don't want to create the snapshot
+ if (runningTaskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "did not create taskSnapshot due to being in PIP");
+ return null;
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"create taskSnapshot surface for task: %d", taskId);
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
similarity index 96%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
index f4828f1..fd56a6e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.splitscreen
+package com.android.wm.shell.flicker
import android.app.Instrumentation
import android.graphics.Point
@@ -40,8 +40,6 @@
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
-import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
-import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
import org.junit.Assert.assertNotNull
internal object SplitScreenUtils {
@@ -114,13 +112,12 @@
}
fun enterSplitViaIntent(
- wmHelper: WindowManagerStateHelper,
- primaryApp: StandardAppHelper,
- secondaryApp: StandardAppHelper
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: StandardAppHelper,
+ secondaryApp: StandardAppHelper
) {
val stringExtras = mapOf(Primary.EXTRA_LAUNCH_ADJACENT to "true")
- primaryApp.launchViaIntent(wmHelper, null, null,
- stringExtras)
+ primaryApp.launchViaIntent(wmHelper, null, null, stringExtras)
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
index 36bbafb..8a85374 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
@@ -16,12 +16,9 @@
package com.android.wm.shell.flicker.pip
-import android.app.Instrumentation
-import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
-import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
@@ -29,13 +26,9 @@
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.BySelector
-import androidx.test.uiautomator.UiObject2
-import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -73,13 +66,11 @@
AutoEnterPipOnGoToHomeTest(flicker) {
private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
/** Second app used to enter split screen mode */
- protected val secondAppForSplitScreen = getSplitScreenApp(instrumentation)
- fun getSplitScreenApp(instrumentation: Instrumentation): StandardAppHelper =
- SimpleAppHelper(
- instrumentation,
- ActivityOptions.SplitScreen.Primary.LABEL,
- ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
- )
+ private val secondAppForSplitScreen = SimpleAppHelper(
+ instrumentation,
+ ActivityOptions.SplitScreen.Primary.LABEL,
+ ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
+ )
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
@@ -88,14 +79,7 @@
secondAppForSplitScreen.launchViaIntent(wmHelper)
pipApp.launchViaIntent(wmHelper)
tapl.goHome()
- enterSplitScreen()
- // wait until split screen is established
- wmHelper
- .StateSyncBuilder()
- .withWindowSurfaceAppeared(pipApp)
- .withWindowSurfaceAppeared(secondAppForSplitScreen)
- .withSplitDividerVisible()
- .waitForAndVerify()
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, pipApp, secondAppForSplitScreen)
pipApp.enableAutoEnterForPipActivity()
}
teardown {
@@ -107,46 +91,6 @@
transitions { tapl.goHome() }
}
- // TODO(b/285400227) merge the code in a common utility - this is copied from SplitScreenUtils
- private val TIMEOUT_MS = 3_000L
- private val overviewSnapshotSelector: BySelector
- get() = By.res(LAUNCHER_UI_PACKAGE_NAME, "snapshot")
- private fun enterSplitScreen() {
- // Note: The initial split position in landscape is different between tablet and phone.
- // In landscape, tablet will let the first app split to right side, and phone will
- // split to left side.
- if (tapl.isTablet) {
- // TAPL's currentTask on tablet is sometimes not what we expected if the overview
- // contains more than 3 task views. We need to use uiautomator directly to find the
- // second task to split.
- tapl.workspace.switchToOverview().overviewActions.clickSplit()
- val snapshots =
- tapl.device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS)
- if (snapshots == null || snapshots.size < 1) {
- error("Fail to find a overview snapshot to split.")
- }
-
- // Find the second task in the upper right corner in split select mode by sorting
- // 'left' in descending order and 'top' in ascending order.
- snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
- t2.getVisibleBounds().left - t1.getVisibleBounds().left
- }
- snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
- t1.getVisibleBounds().top - t2.getVisibleBounds().top
- }
- snapshots[0].click()
- } else {
- tapl.workspace
- .switchToOverview()
- .currentTask
- .tapMenu()
- .tapSplitMenuItem()
- .currentTask
- .open()
- }
- SystemClock.sleep(TIMEOUT_MS)
- }
-
@Presubmit
@Test
override fun pipOverlayLayerAppearThenDisappear() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/SplitScreenUtils.kt
similarity index 95%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/SplitScreenUtils.kt
index f4828f1..e640dc4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/SplitScreenUtils.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.splitscreen
+package com.android.wm.shell.flicker.service.splitscreen
import android.app.Instrumentation
import android.graphics.Point
@@ -39,12 +39,11 @@
import com.android.server.wm.flicker.helpers.NotificationAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
import org.junit.Assert.assertNotNull
-internal object SplitScreenUtils {
+object SplitScreenUtils {
private const val TIMEOUT_MS = 3_000L
private const val DRAG_DURATION_MS = 1_000L
private const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
@@ -113,17 +112,6 @@
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
- fun enterSplitViaIntent(
- wmHelper: WindowManagerStateHelper,
- primaryApp: StandardAppHelper,
- secondaryApp: StandardAppHelper
- ) {
- val stringExtras = mapOf(Primary.EXTRA_LAUNCH_ADJACENT to "true")
- primaryApp.launchViaIntent(wmHelper, null, null,
- stringExtras)
- waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
- }
-
fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice) {
// Note: The initial split position in landscape is different between tablet and phone.
// In landscape, tablet will let the first app split to right side, and phone will
@@ -159,6 +147,17 @@
SystemClock.sleep(TIMEOUT_MS)
}
+ fun enterSplitViaIntent(
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: StandardAppHelper,
+ secondaryApp: StandardAppHelper
+ ) {
+ val stringExtras =
+ mapOf(ActivityOptions.SplitScreen.Primary.EXTRA_LAUNCH_ADJACENT to "true")
+ primaryApp.launchViaIntent(wmHelper, null, null, stringExtras)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
fun dragFromNotificationToSplit(
instrumentation: Instrumentation,
device: UiDevice,
@@ -326,14 +325,14 @@
dividerBar.drag(
Point(
if (dragToRight) {
- displayBounds.right
+ displayBounds.width * 4 / 5
} else {
- displayBounds.left
+ displayBounds.width * 1 / 5
},
if (dragToBottom) {
- displayBounds.bottom
+ displayBounds.height * 4 / 5
} else {
- displayBounds.top
+ displayBounds.height * 1 / 5
}
)
)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt
new file mode 100644
index 0000000..964a785
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
+ @ExpectedScenarios([])
+ @Test
+ override fun copyContentInSplit() = super.copyContentInSplit()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt
new file mode 100644
index 0000000..bc30d41
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
+ @ExpectedScenarios([])
+ @Test
+ override fun copyContentInSplit() = super.copyContentInSplit()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
new file mode 100644
index 0000000..8cb25fe
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DismissSplitScreenByDividerGesturalNavLandscape :
+ DismissSplitScreenByDivider(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @Test
+ override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
new file mode 100644
index 0000000..fa1be63
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DismissSplitScreenByDividerGesturalNavPortrait :
+ DismissSplitScreenByDivider(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @Test
+ override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
new file mode 100644
index 0000000..aa35237
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DismissSplitScreenByGoHomeGesturalNavLandscape :
+ DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @Test
+ override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
new file mode 100644
index 0000000..e195360
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DismissSplitScreenByGoHomeGesturalNavPortrait :
+ DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @Test
+ override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
new file mode 100644
index 0000000..c1b3aad
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_RESIZE"])
+ @Test
+ override fun dragDividerToResize() = super.dragDividerToResize()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
new file mode 100644
index 0000000..c6e2e85
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_RESIZE"])
+ @Test
+ override fun dragDividerToResize() = super.dragDividerToResize()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
new file mode 100644
index 0000000..5f771c7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
+ EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
new file mode 100644
index 0000000..729a401
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
+ EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
new file mode 100644
index 0000000..6e4cf9f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
+ EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromNotification() =
+ super.enterSplitScreenByDragFromNotification()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
new file mode 100644
index 0000000..cc28702
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
+ EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromNotification() =
+ super.enterSplitScreenByDragFromNotification()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
new file mode 100644
index 0000000..736604f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
+ EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromShortcut() = super.enterSplitScreenByDragFromShortcut()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
new file mode 100644
index 0000000..8df8dfa
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
+ EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromShortcut() = super.enterSplitScreenByDragFromShortcut()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
new file mode 100644
index 0000000..378f055
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
+ EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromTaskbar() = super.enterSplitScreenByDragFromTaskbar()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
new file mode 100644
index 0000000..b33d262
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
+ EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromTaskbar() = super.enterSplitScreenByDragFromTaskbar()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
new file mode 100644
index 0000000..b1d3858
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenFromOverviewGesturalNavLandscape :
+ EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenFromOverview() = super.enterSplitScreenFromOverview()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
new file mode 100644
index 0000000..6d824c7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenFromOverviewGesturalNavPortrait :
+ EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenFromOverview() = super.enterSplitScreenFromOverview()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
new file mode 100644
index 0000000..f1d3d0c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchAppByDoubleTapDividerGesturalNavLandscape :
+ SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios([])
+ @Test
+ override fun switchAppByDoubleTapDivider() = super.switchAppByDoubleTapDivider()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
new file mode 100644
index 0000000..a867bac
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchAppByDoubleTapDividerGesturalNavPortrait :
+ SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios([])
+ @Test
+ override fun switchAppByDoubleTapDivider() = super.switchAppByDoubleTapDivider()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
new file mode 100644
index 0000000..76247ba
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
+ SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBackToSplitFromAnotherApp() = super.switchBackToSplitFromAnotherApp()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
new file mode 100644
index 0000000..e179da8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
+ SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBackToSplitFromAnotherApp() = super.switchBackToSplitFromAnotherApp()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
new file mode 100644
index 0000000..20f554f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromHomeGesturalNavLandscape :
+ SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBackToSplitFromHome() = super.switchBackToSplitFromHome()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
new file mode 100644
index 0000000..f7776ee
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromHomeGesturalNavPortrait :
+ SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBackToSplitFromHome() = super.switchBackToSplitFromHome()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
new file mode 100644
index 0000000..00f6073
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromRecentGesturalNavLandscape :
+ SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
new file mode 100644
index 0000000..b3340e7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.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.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromRecentGesturalNavPortrait :
+ SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
new file mode 100644
index 0000000..3da61e5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBetweenSplitPairsGesturalNavLandscape : SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
new file mode 100644
index 0000000..627ae18
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBetweenSplitPairsGesturalNavPortrait : SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
new file mode 100644
index 0000000..7cbc1c3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.service.splitscreen.flicker
+
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
new file mode 100644
index 0000000..2eb81e0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.service.splitscreen.flicker
+
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
new file mode 100644
index 0000000..76ad6b9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class CopyContentInSplit
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+ private val textEditApp = SplitScreenUtils.getIme(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp)
+ }
+
+ @Test
+ open fun copyContentInSplit() {
+ SplitScreenUtils.copyContentInSplit(instrumentation, device, primaryApp, textEditApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
new file mode 100644
index 0000000..25182b4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class DismissSplitScreenByDivider
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun dismissSplitScreenByDivider() {
+ if (tapl.isTablet) {
+ SplitScreenUtils.dragDividerToDismissSplit(
+ device,
+ wmHelper,
+ dragToRight = false,
+ dragToBottom = true
+ )
+ } else {
+ SplitScreenUtils.dragDividerToDismissSplit(
+ device,
+ wmHelper,
+ dragToRight = true,
+ dragToBottom = true
+ )
+ }
+ wmHelper.StateSyncBuilder().withFullScreenApp(secondaryApp).waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
new file mode 100644
index 0000000..000b628
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class DismissSplitScreenByGoHome
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun dismissSplitScreenByGoHome() {
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
new file mode 100644
index 0000000..dd9ff3c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class DragDividerToResize
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun dragDividerToResize() {
+ SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
new file mode 100644
index 0000000..4bbb9aa
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenByDragFromAllApps
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun enterSplitScreenByDragFromAllApps() {
+ tapl.launchedAppState.taskbar
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
new file mode 100644
index 0000000..a2b7526
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenByDragFromNotification
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+ private val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ // Send a notification
+ sendNotificationApp.launchViaIntent(wmHelper)
+ sendNotificationApp.postNotification(wmHelper)
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun enterSplitScreenByDragFromNotification() {
+ SplitScreenUtils.dragFromNotificationToSplit(instrumentation, device, wmHelper)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, sendNotificationApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ sendNotificationApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
new file mode 100644
index 0000000..1ccd813
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenByDragFromShortcut
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(tapl.isTablet)
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ tapl.goHome()
+ SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
+ primaryApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun enterSplitScreenByDragFromShortcut() {
+ tapl.launchedAppState.taskbar
+ .getAppIcon(secondaryApp.appName)
+ .openDeepShortcutMenu()
+ .getMenuItem("Split Screen Secondary Activity")
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+
+ // TODO: Do we want this check in here? Add to the other tests?
+ // flicker.splitScreenEntered(
+ // primaryApp,
+ // secondaryApp,
+ // fromOtherApp = false,
+ // appExistAtStart = false
+ // )
+ }
+
+ @After
+ fun teardwon() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
new file mode 100644
index 0000000..664786b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenByDragFromTaskbar
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ tapl.goHome()
+ SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
+ primaryApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun enterSplitScreenByDragFromTaskbar() {
+ tapl.launchedAppState.taskbar
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
new file mode 100644
index 0000000..88fd084
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenFromOverview
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ primaryApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ }
+
+ @Test
+ open fun enterSplitScreenFromOverview() {
+ SplitScreenUtils.splitFromOverview(tapl, device)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SplitScreenUtils.kt
similarity index 86%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SplitScreenUtils.kt
index f4828f1..83a18e8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SplitScreenUtils.kt
@@ -14,20 +14,30 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.splitscreen
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
import android.app.Instrumentation
import android.graphics.Point
import android.os.SystemClock
+import android.platform.test.rule.NavigationModeRule
+import android.platform.test.rule.PressHomeRule
+import android.platform.test.rule.UnlockScreenRule
+import android.tools.common.NavBar
+import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.IComponentMatcher
import android.tools.common.traces.component.IComponentNameMatcher
+import android.tools.device.apphelpers.MessagingAppHelper
import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.device.flicker.rules.LaunchAppRule
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import android.tools.device.traces.parsers.toFlickerComponent
import android.view.InputDevice
import android.view.MotionEvent
import android.view.ViewConfiguration
+import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiDevice
@@ -39,12 +49,12 @@
import com.android.server.wm.flicker.helpers.NotificationAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
import org.junit.Assert.assertNotNull
+import org.junit.rules.RuleChain
-internal object SplitScreenUtils {
+object SplitScreenUtils {
private const val TIMEOUT_MS = 3_000L
private const val DRAG_DURATION_MS = 1_000L
private const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
@@ -63,6 +73,30 @@
private val overviewSnapshotSelector: BySelector
get() = By.res(LAUNCHER_UI_PACKAGE_NAME, OVERVIEW_SNAPSHOT)
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+
+ fun testSetupRule(navigationMode: () -> NavBar, rotation: () -> Rotation): RuleChain {
+ return RuleChain.outerRule(UnlockScreenRule())
+ .around(
+ NavigationModeRule(
+ navigationMode().value,
+ /* changeNavigationModeAfterTest */ false
+ )
+ )
+ .around(
+ LaunchAppRule(MessagingAppHelper(instrumentation), clearCacheAfterParsing = false)
+ )
+ .around(RemoveAllTasksButHomeRule())
+ .around(
+ ChangeDisplayOrientationRule(
+ rotation(),
+ resetOrientationAfterTest = false,
+ clearCacheAfterParsing = false
+ )
+ )
+ .around(PressHomeRule())
+ }
+
fun getPrimary(instrumentation: Instrumentation): StandardAppHelper =
SimpleAppHelper(
instrumentation,
@@ -113,17 +147,6 @@
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
- fun enterSplitViaIntent(
- wmHelper: WindowManagerStateHelper,
- primaryApp: StandardAppHelper,
- secondaryApp: StandardAppHelper
- ) {
- val stringExtras = mapOf(Primary.EXTRA_LAUNCH_ADJACENT to "true")
- primaryApp.launchViaIntent(wmHelper, null, null,
- stringExtras)
- waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
- }
-
fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice) {
// Note: The initial split position in landscape is different between tablet and phone.
// In landscape, tablet will let the first app split to right side, and phone will
@@ -159,6 +182,17 @@
SystemClock.sleep(TIMEOUT_MS)
}
+ fun enterSplitViaIntent(
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: StandardAppHelper,
+ secondaryApp: StandardAppHelper
+ ) {
+ val stringExtras =
+ mapOf(ActivityOptions.SplitScreen.Primary.EXTRA_LAUNCH_ADJACENT to "true")
+ primaryApp.launchViaIntent(wmHelper, null, null, stringExtras)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
fun dragFromNotificationToSplit(
instrumentation: Instrumentation,
device: UiDevice,
@@ -326,14 +360,14 @@
dividerBar.drag(
Point(
if (dragToRight) {
- displayBounds.right
+ displayBounds.width * 4 / 5
} else {
- displayBounds.left
+ displayBounds.width * 1 / 5
},
if (dragToBottom) {
- displayBounds.bottom
+ displayBounds.height * 4 / 5
} else {
- displayBounds.top
+ displayBounds.height * 1 / 5
}
)
)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
new file mode 100644
index 0000000..e5501f4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -0,0 +1,151 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.graphics.Point
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.helpers.WindowUtils
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchAppByDoubleTapDivider
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ tapl.workspace.switchToOverview().dismissAllTasks()
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun switchAppByDoubleTapDivider() {
+ SplitScreenUtils.doubleTapDividerToSwitch(device)
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+
+ waitForLayersToSwitch(wmHelper)
+ waitForWindowsToSwitch(wmHelper)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+
+ private fun waitForWindowsToSwitch(wmHelper: WindowManagerStateHelper) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("appWindowsSwitched") {
+ val primaryAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ primaryApp.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val secondaryAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ secondaryApp.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+
+ if (isLandscape(rotation)) {
+ return@add if (isTablet()) {
+ secondaryAppWindow.frame.right <= primaryAppWindow.frame.left
+ } else {
+ primaryAppWindow.frame.right <= secondaryAppWindow.frame.left
+ }
+ } else {
+ return@add if (isTablet()) {
+ primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
+ } else {
+ primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
+ }
+ }
+ }
+ .waitForAndVerify()
+ }
+
+ private fun waitForLayersToSwitch(wmHelper: WindowManagerStateHelper) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("appLayersSwitched") {
+ val primaryAppLayer =
+ it.layerState.visibleLayers.firstOrNull { window ->
+ primaryApp.layerMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val secondaryAppLayer =
+ it.layerState.visibleLayers.firstOrNull { window ->
+ secondaryApp.layerMatchesAnyOf(window)
+ }
+ ?: return@add false
+
+ val primaryVisibleRegion = primaryAppLayer.visibleRegion?.bounds ?: return@add false
+ val secondaryVisibleRegion =
+ secondaryAppLayer.visibleRegion?.bounds ?: return@add false
+
+ if (isLandscape(rotation)) {
+ return@add if (isTablet()) {
+ secondaryVisibleRegion.right <= primaryVisibleRegion.left
+ } else {
+ primaryVisibleRegion.right <= secondaryVisibleRegion.left
+ }
+ } else {
+ return@add if (isTablet()) {
+ primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
+ } else {
+ primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
+ }
+ }
+ }
+ .waitForAndVerify()
+ }
+
+ private fun isLandscape(rotation: Rotation): Boolean {
+ val displayBounds = WindowUtils.getDisplayBounds(rotation)
+ return displayBounds.width > displayBounds.height
+ }
+
+ private fun isTablet(): Boolean {
+ val sizeDp: Point = device.displaySizeDp
+ val LARGE_SCREEN_DP_THRESHOLD = 600
+ return sizeDp.x >= LARGE_SCREEN_DP_THRESHOLD && sizeDp.y >= LARGE_SCREEN_DP_THRESHOLD
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
new file mode 100644
index 0000000..b3f1e87
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBackToSplitFromAnotherApp
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+ private val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ thirdApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
+ }
+
+ @Test
+ open fun switchBackToSplitFromAnotherApp() {
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
new file mode 100644
index 0000000..d112116
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBackToSplitFromHome
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+
+ @Test
+ open fun switchBackToSplitFromHome() {
+ tapl.workspace.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
new file mode 100644
index 0000000..9ab924c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBackToSplitFromRecent
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+
+ @Test
+ open fun switchBackToSplitFromRecent() {
+ tapl.workspace.switchToOverview().currentTask.open()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
new file mode 100644
index 0000000..b694dfa
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBetweenSplitPairs
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+ private val thirdApp = SplitScreenUtils.getIme(instrumentation)
+ private val fourthApp = SplitScreenUtils.getSendNotification(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
+ }
+
+ @Test
+ open fun switchBetweenSplitPairs() {
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ thirdApp.exit(wmHelper)
+ fourthApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
new file mode 100644
index 0000000..f78b788
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class UnlockKeyguardToSplitScreen {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule =
+ SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { Rotation.ROTATION_0 })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(Rotation.ROTATION_0.value)
+
+ SplitScreenUtils.enterSplitViaIntent(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun unlockKeyguardToSplitScreen() {
+ device.sleep()
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+ device.wakeUp()
+ device.pressMenu()
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
index 580b153..d3434a5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
@@ -21,6 +21,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.BaseBenchmarkTest
+import com.android.wm.shell.flicker.SplitScreenUtils
abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(flicker) {
protected val context: Context = instrumentation.context
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index d1ca9ea..9c68aa4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -25,7 +25,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
index 73acb1f..21ac783 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -25,7 +25,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
index 86ffd2a..931bff6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -25,7 +25,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
index dfde3b6..7fa2c0b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
@@ -24,7 +24,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
index d13e413..952051f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
index 1d41669..1de1c0c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
index b4bafa7..929c7ea 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
index da44ecd..9f829c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
index af06d6d..1d5518f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
@@ -25,7 +25,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index 23156b5..a7fb93e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -28,7 +28,7 @@
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
index 2d810d3..8358aff 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
index f6df1e4..b63c765 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
index ba46bdc..ce5a409b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
index 0d871e5..9821bfa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
@@ -24,7 +24,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -64,8 +64,7 @@
thisTransition(this)
}
- @PlatinumTest(focusArea = "sysui")
- @Presubmit @Test open fun cujCompleted() {}
+ @PlatinumTest(focusArea = "sysui") @Presubmit @Test open fun cujCompleted() {}
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
index 7952b71..4fc4627 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
@@ -23,7 +23,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java
index 02e6b8c..ec84d7e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java
@@ -23,7 +23,9 @@
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_FULLSCREEN;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_MOVE;
-import static org.junit.Assert.assertTrue;
+import static java.util.Collections.EMPTY_LIST;
+
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -34,7 +36,6 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.Log;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.pip.PipMediaController;
@@ -46,7 +47,9 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
/**
* Unit tests for {@link TvPipActionsProvider}
@@ -69,35 +72,38 @@
@Mock
private PendingIntent mMockPendingIntent;
- private RemoteAction createRemoteAction(int identifier) {
+ private int mNumberOfRemoteActionsCreated = 0;
+
+ private RemoteAction createRemoteAction() {
+ final int identifier = mNumberOfRemoteActionsCreated++;
return new RemoteAction(mMockIcon, "" + identifier, "" + identifier, mMockPendingIntent);
}
private List<RemoteAction> createRemoteActions(int numberOfActions) {
List<RemoteAction> actions = new ArrayList<>();
for (int i = 0; i < numberOfActions; i++) {
- actions.add(createRemoteAction(i));
+ actions.add(createRemoteAction());
}
return actions;
}
- private boolean checkActionsMatch(List<TvPipAction> actions, int[] actionTypes) {
- for (int i = 0; i < actions.size(); i++) {
- int type = actions.get(i).getActionType();
- if (type != actionTypes[i]) {
- Log.e(TAG, "Action at index " + i + ": found " + type
- + ", expected " + actionTypes[i]);
- return false;
- }
- }
- return true;
+ private void assertActionTypes(List<Integer> expected, List<Integer> actual) {
+ assertEquals(getActionTypesStrings(expected), getActionTypesStrings(actual));
+ }
+
+ private static List<String> getActionTypesStrings(List<Integer> actionTypes) {
+ return actionTypes.stream().map(a -> TvPipAction.getActionTypeString(a))
+ .collect(Collectors.toList());
+ }
+
+ private List<Integer> getActionsTypes() {
+ return mActionsProvider.getActionsList().stream().map(a -> a.getActionType())
+ .collect(Collectors.toList());
}
@Before
public void setUp() {
- if (!isTelevision()) {
- return;
- }
+ assumeTelevision();
MockitoAnnotations.initMocks(this);
mActionsProvider = new TvPipActionsProvider(mContext, mMockPipMediaController,
mMockSystemActionsHandler);
@@ -105,57 +111,51 @@
@Test
public void defaultSystemActions_regularPip() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE}));
+ assertActionTypes(Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
}
@Test
public void defaultSystemActions_expandedPip() {
- assumeTelevision();
mActionsProvider.updateExpansionEnabled(true);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE),
+ getActionsTypes());
}
@Test
public void expandedPip_enableExpansion_enable() {
- assumeTelevision();
// PiP has expanded PiP disabled.
- mActionsProvider.updateExpansionEnabled(false);
-
mActionsProvider.addListener(mMockListener);
mActionsProvider.updateExpansionEnabled(true);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ 1, /* updated= */ 0, /* index= */ 3);
}
@Test
public void expandedPip_enableExpansion_disable() {
- assumeTelevision();
mActionsProvider.updateExpansionEnabled(true);
mActionsProvider.addListener(mMockListener);
mActionsProvider.updateExpansionEnabled(false);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ -1, /* updated= */ 0, /* index= */ 3);
}
@Test
public void expandedPip_enableExpansion_AlreadyEnabled() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(true);
-
mActionsProvider.addListener(mMockListener);
mActionsProvider.updateExpansionEnabled(true);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE),
+ getActionsTypes());
}
private void check_expandedPip_updateExpansionState(
@@ -167,8 +167,9 @@
mActionsProvider.addListener(mMockListener);
mActionsProvider.updatePipExpansionState(endExpansion);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE),
+ getActionsTypes());
if (updateExpected) {
verify(mMockListener).onActionsChanged(0, 1, 3);
@@ -180,7 +181,6 @@
@Test
public void expandedPip_toggleExpansion_collapse() {
- assumeTelevision();
check_expandedPip_updateExpansionState(
/* startExpansion= */ true,
/* endExpansion= */ false,
@@ -189,7 +189,6 @@
@Test
public void expandedPip_toggleExpansion_expand() {
- assumeTelevision();
check_expandedPip_updateExpansionState(
/* startExpansion= */ false,
/* endExpansion= */ true,
@@ -198,7 +197,6 @@
@Test
public void expandedPiP_updateExpansionState_alreadyExpanded() {
- assumeTelevision();
check_expandedPip_updateExpansionState(
/* startExpansion= */ true,
/* endExpansion= */ true,
@@ -207,7 +205,6 @@
@Test
public void expandedPiP_updateExpansionState_alreadyCollapsed() {
- assumeTelevision();
check_expandedPip_updateExpansionState(
/* startExpansion= */ false,
/* endExpansion= */ false,
@@ -216,8 +213,6 @@
@Test
public void regularPiP_updateExpansionState_setCollapsed() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.updatePipExpansionState(/* expanded= */ false);
mActionsProvider.addListener(mMockListener);
@@ -229,153 +224,207 @@
@Test
public void customActions_added() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.addListener(mMockListener);
mActionsProvider.setAppActions(createRemoteActions(2), null);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ 2, /* updated= */ 0, /* index= */ 2);
}
@Test
public void customActions_replacedMore() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.setAppActions(createRemoteActions(2), null);
mActionsProvider.addListener(mMockListener);
mActionsProvider.setAppActions(createRemoteActions(3), null);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_CUSTOM, ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_CUSTOM, ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ 1, /* updated= */ 2, /* index= */ 2);
}
@Test
public void customActions_replacedLess() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.setAppActions(createRemoteActions(2), null);
mActionsProvider.addListener(mMockListener);
- mActionsProvider.setAppActions(createRemoteActions(0), null);
+ mActionsProvider.setAppActions(EMPTY_LIST, null);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ -2, /* updated= */ 0, /* index= */ 2);
}
@Test
public void customCloseAdded() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
-
List<RemoteAction> customActions = new ArrayList<>();
mActionsProvider.setAppActions(customActions, null);
mActionsProvider.addListener(mMockListener);
- mActionsProvider.setAppActions(customActions, createRemoteAction(0));
+ mActionsProvider.setAppActions(customActions, createRemoteAction());
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ 0, /* updated= */ 1, /* index= */ 1);
}
@Test
public void customClose_matchesOtherCustomAction() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
-
List<RemoteAction> customActions = createRemoteActions(2);
- RemoteAction customClose = createRemoteAction(/* id= */ 10);
+ RemoteAction customClose = createRemoteAction();
customActions.add(customClose);
mActionsProvider.addListener(mMockListener);
mActionsProvider.setAppActions(customActions, customClose);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ 0, /* updated= */ 1, /* index= */ 1);
verify(mMockListener).onActionsChanged(/* added= */ 2, /* updated= */ 0, /* index= */ 2);
}
@Test
public void mediaActions_added_whileCustomActionsExist() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.setAppActions(createRemoteActions(2), null);
mActionsProvider.addListener(mMockListener);
mActionsProvider.onMediaActionsChanged(createRemoteActions(3));
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener, times(0)).onActionsChanged(anyInt(), anyInt(), anyInt());
}
@Test
public void customActions_removed_whileMediaActionsExist() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.onMediaActionsChanged(createRemoteActions(2));
mActionsProvider.setAppActions(createRemoteActions(3), null);
- mActionsProvider.addListener(mMockListener);
- mActionsProvider.setAppActions(createRemoteActions(0), null);
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_CUSTOM, ACTION_MOVE),
+ getActionsTypes());
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_MOVE}));
+ mActionsProvider.addListener(mMockListener);
+ mActionsProvider.setAppActions(EMPTY_LIST, null);
+
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ -1, /* updated= */ 2, /* index= */ 2);
}
@Test
public void customCloseOnly_mediaActionsShowing() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.onMediaActionsChanged(createRemoteActions(2));
mActionsProvider.addListener(mMockListener);
- mActionsProvider.setAppActions(createRemoteActions(0), createRemoteAction(5));
+ mActionsProvider.setAppActions(EMPTY_LIST, createRemoteAction());
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ 0, /* updated= */ 1, /* index= */ 1);
}
@Test
public void customActions_showDisabledActions() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
-
List<RemoteAction> customActions = createRemoteActions(2);
customActions.get(0).setEnabled(false);
mActionsProvider.setAppActions(customActions, null);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
}
@Test
public void mediaActions_hideDisabledActions() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
-
List<RemoteAction> customActions = createRemoteActions(2);
customActions.get(0).setEnabled(false);
mActionsProvider.onMediaActionsChanged(customActions);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_MOVE),
+ getActionsTypes());
+ }
+
+ @Test
+ public void reset_mediaActions() {
+ List<RemoteAction> customActions = createRemoteActions(2);
+ customActions.get(0).setEnabled(false);
+ mActionsProvider.onMediaActionsChanged(customActions);
+
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_MOVE),
+ getActionsTypes());
+
+ mActionsProvider.reset();
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
+ }
+
+ @Test
+ public void reset_customActions() {
+ List<RemoteAction> customActions = createRemoteActions(2);
+ customActions.get(0).setEnabled(false);
+ mActionsProvider.setAppActions(customActions, null);
+
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
+
+ mActionsProvider.reset();
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
+ }
+
+ @Test
+ public void reset_customClose() {
+ mActionsProvider.setAppActions(EMPTY_LIST, createRemoteAction());
+
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_MOVE),
+ getActionsTypes());
+
+ mActionsProvider.reset();
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
+ }
+
+ @Test
+ public void reset_All() {
+ mActionsProvider.setAppActions(createRemoteActions(2), createRemoteAction());
+ mActionsProvider.onMediaActionsChanged(createRemoteActions(3));
+
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
+
+ mActionsProvider.reset();
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
}
}
diff --git a/packages/CarrierDefaultApp/assets/slice_purchase_test.html b/packages/CarrierDefaultApp/assets/slice_purchase_test.html
index 917276b..ad18a9d 100644
--- a/packages/CarrierDefaultApp/assets/slice_purchase_test.html
+++ b/packages/CarrierDefaultApp/assets/slice_purchase_test.html
@@ -81,5 +81,7 @@
Dismiss flow
</button>
<p id="dismiss_flow"></p>
+
+ <h2>Test <a href="http://www.google.com">hyperlink</a></h2>
</body>
</html>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java
index 2530257d6..b100980 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java
@@ -29,6 +29,7 @@
import android.view.KeyEvent;
import android.webkit.CookieManager;
import android.webkit.WebView;
+import android.webkit.WebViewClient;
import com.android.phone.slice.SlicePurchaseController;
@@ -113,8 +114,10 @@
return;
}
- // Create and configure WebView
- setupWebView();
+ // Clear any cookies that might be persisted from previous sessions before loading WebView
+ CookieManager.getInstance().removeAllCookies(value -> {
+ setupWebView();
+ });
}
protected void onPurchaseSuccessful() {
@@ -176,12 +179,7 @@
private void setupWebView() {
// Create WebView
mWebView = new WebView(this);
-
- // Clear any cookies and state that might be saved from previous sessions
- CookieManager.getInstance().removeAllCookies(null);
- CookieManager.getInstance().flush();
- mWebView.clearCache(true);
- mWebView.clearHistory();
+ mWebView.setWebViewClient(new WebViewClient());
// Enable JavaScript for the carrier purchase website to send results back to
// the slice purchase application.
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index 00dd8cc..612a928 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -284,10 +284,10 @@
* Optional key to use for this tile.
*/
public String getKey(Context context) {
+ ensureMetadataNotStale(context);
if (!hasKey()) {
return null;
}
- ensureMetadataNotStale(context);
if (mMetaData.get(META_DATA_PREFERENCE_KEYHINT) instanceof Integer) {
return context.getResources().getString(mMetaData.getInt(META_DATA_PREFERENCE_KEYHINT));
} else {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index cd6609e..963bd9d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -15,6 +15,8 @@
*/
package com.android.settingslib.media;
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
+
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
@@ -22,6 +24,7 @@
import android.media.AudioManager;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.RouteListingPreference;
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -39,7 +42,13 @@
BluetoothMediaDevice(Context context, CachedBluetoothDevice device,
MediaRouter2Manager routerManager, MediaRoute2Info info, String packageName) {
- super(context, routerManager, info, packageName, null);
+ this(context, device, routerManager, info, packageName, null);
+ }
+
+ BluetoothMediaDevice(Context context, CachedBluetoothDevice device,
+ MediaRouter2Manager routerManager, MediaRoute2Info info, String packageName,
+ RouteListingPreference.Item item) {
+ super(context, routerManager, info, packageName, item);
mCachedDevice = device;
mAudioManager = context.getSystemService(AudioManager.class);
initDeviceRecord();
@@ -58,6 +67,12 @@
}
@Override
+ public int getSelectionBehavior() {
+ // We don't allow apps to override the selection behavior of system routes.
+ return SELECTION_BEHAVIOR_TRANSFER;
+ }
+
+ @Override
public Drawable getIcon() {
return BluetoothUtils.isAdvancedUntetheredDevice(mCachedDevice.getDevice())
? mContext.getDrawable(R.drawable.ic_earbuds_advanced)
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index d8af4cc..3e864f9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -174,11 +174,13 @@
MediaRoute2Info route, RouteListingPreference.Item routeListingPreferenceItem);
@NonNull
- protected abstract PhoneMediaDevice createPhoneMediaDevice(MediaRoute2Info route);
+ protected abstract PhoneMediaDevice createPhoneMediaDevice(MediaRoute2Info route,
+ RouteListingPreference.Item routeListingPreferenceItem);
@NonNull
protected abstract BluetoothMediaDevice createBluetoothMediaDevice(
- MediaRoute2Info route, CachedBluetoothDevice cachedDevice);
+ MediaRoute2Info route, CachedBluetoothDevice cachedDevice,
+ RouteListingPreference.Item routeListingPreferenceItem);
protected final void rebuildDeviceList() {
mMediaDevices.clear();
@@ -593,7 +595,8 @@
case TYPE_HDMI:
case TYPE_WIRED_HEADSET:
case TYPE_WIRED_HEADPHONES:
- mediaDevice = createPhoneMediaDevice(route);
+ mediaDevice = createPhoneMediaDevice(route,
+ mPreferenceItemMap.getOrDefault(route.getId(), null));
break;
case TYPE_HEARING_AID:
case TYPE_BLUETOOTH_A2DP:
@@ -603,7 +606,8 @@
final CachedBluetoothDevice cachedDevice =
mBluetoothManager.getCachedDeviceManager().findDevice(device);
if (cachedDevice != null) {
- mediaDevice = createBluetoothMediaDevice(route, cachedDevice);
+ mediaDevice = createBluetoothMediaDevice(route, cachedDevice,
+ mPreferenceItemMap.getOrDefault(route.getId(), null));
}
break;
case TYPE_REMOTE_AUDIO_VIDEO_RECEIVER:
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index dcbfb45..c86a943 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -185,16 +185,20 @@
@Override
@NonNull
- protected PhoneMediaDevice createPhoneMediaDevice(MediaRoute2Info route) {
- return new PhoneMediaDevice(mContext, mRouterManager, route, mPackageName);
+ protected PhoneMediaDevice createPhoneMediaDevice(MediaRoute2Info route,
+ RouteListingPreference.Item routeListingPreferenceItem) {
+ return new PhoneMediaDevice(mContext, mRouterManager, route, mPackageName,
+ routeListingPreferenceItem);
}
@Override
@NonNull
protected BluetoothMediaDevice createBluetoothMediaDevice(
- MediaRoute2Info route, CachedBluetoothDevice cachedDevice) {
+ MediaRoute2Info route, CachedBluetoothDevice cachedDevice,
+ RouteListingPreference.Item routeListingPreferenceItem) {
return new BluetoothMediaDevice(
- mContext, cachedDevice, mRouterManager, route, mPackageName);
+ mContext, cachedDevice, mRouterManager, route, mPackageName,
+ routeListingPreferenceItem);
}
@VisibleForTesting
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 34519c9..accd88c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -24,10 +24,13 @@
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
+
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.RouteListingPreference;
import androidx.annotation.VisibleForTesting;
@@ -51,7 +54,12 @@
PhoneMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
String packageName) {
- super(context, routerManager, info, packageName, null);
+ this(context, routerManager, info, packageName, null);
+ }
+
+ PhoneMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
+ String packageName, RouteListingPreference.Item item) {
+ super(context, routerManager, info, packageName, item);
mDeviceIconUtil = new DeviceIconUtil();
initDeviceRecord();
}
@@ -86,6 +94,12 @@
}
@Override
+ public int getSelectionBehavior() {
+ // We don't allow apps to override the selection behavior of system routes.
+ return SELECTION_BEHAVIOR_TRANSFER;
+ }
+
+ @Override
public String getSummary() {
return mSummary;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoEntity.java b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoEntity.java
index c40388f..361a2461 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoEntity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoEntity.java
@@ -16,27 +16,23 @@
package com.android.settingslib.mobile.dataservice;
-import static androidx.room.ForeignKey.CASCADE;
-
import android.text.TextUtils;
-import java.util.Objects;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
-import androidx.room.ForeignKey;
-import androidx.room.Index;
import androidx.room.PrimaryKey;
+import java.util.Objects;
+
@Entity(tableName = DataServiceUtils.SubscriptionInfoData.TABLE_NAME)
public class SubscriptionInfoEntity {
public SubscriptionInfoEntity(@NonNull String subId, int simSlotIndex, int carrierId,
String displayName, String carrierName, int dataRoaming, String mcc, String mnc,
String countryIso, boolean isEmbedded, int cardId, int portIndex,
boolean isOpportunistic, @Nullable String groupUUID, int subscriptionType,
- String uniqueName, boolean isSubscriptionVisible, String formattedPhoneNumber,
+ String uniqueName, boolean isSubscriptionVisible, @Nullable String formattedPhoneNumber,
boolean isFirstRemovableSubscription, boolean isDefaultSubscriptionSelection,
boolean isValidSubscription, boolean isUsableSubscription,
boolean isActiveSubscriptionId, boolean isAvailableSubscription,
@@ -123,6 +119,7 @@
public boolean isSubscriptionVisible;
@ColumnInfo(name = DataServiceUtils.SubscriptionInfoData.COLUMN_FORMATTED_PHONE_NUMBER)
+ @Nullable
public String formattedPhoneNumber;
@ColumnInfo(name = DataServiceUtils.SubscriptionInfoData.COLUMN_IS_FIRST_REMOVABLE_SUBSCRIPTION)
@@ -165,33 +162,32 @@
@Override
public int hashCode() {
- int result = 17;
- result = 31 * result + subId.hashCode();
- result = 31 * result + simSlotIndex;
- result = 31 * result + carrierId;
- result = 31 * result + displayName.hashCode();
- result = 31 * result + carrierName.hashCode();
- result = 31 * result + dataRoaming;
- result = 31 * result + mcc.hashCode();
- result = 31 * result + mnc.hashCode();
- result = 31 * result + countryIso.hashCode();
- result = 31 * result + Boolean.hashCode(isEmbedded);
- result = 31 * result + cardId;
- result = 31 * result + portIndex;
- result = 31 * result + Boolean.hashCode(isOpportunistic);
- result = 31 * result + groupUUID.hashCode();
- result = 31 * result + subscriptionType;
- result = 31 * result + uniqueName.hashCode();
- result = 31 * result + Boolean.hashCode(isSubscriptionVisible);
- result = 31 * result + formattedPhoneNumber.hashCode();
- result = 31 * result + Boolean.hashCode(isFirstRemovableSubscription);
- result = 31 * result + Boolean.hashCode(isDefaultSubscriptionSelection);
- result = 31 * result + Boolean.hashCode(isValidSubscription);
- result = 31 * result + Boolean.hashCode(isUsableSubscription);
- result = 31 * result + Boolean.hashCode(isActiveSubscriptionId);
- result = 31 * result + Boolean.hashCode(isAvailableSubscription);
- result = 31 * result + Boolean.hashCode(isActiveDataSubscriptionId);
- return result;
+ return Objects.hash(
+ subId,
+ simSlotIndex,
+ carrierId,
+ displayName,
+ carrierName,
+ dataRoaming,
+ mcc,
+ mnc,
+ countryIso,
+ isEmbedded,
+ cardId,
+ portIndex,
+ isOpportunistic,
+ groupUUID,
+ subscriptionType,
+ uniqueName,
+ isSubscriptionVisible,
+ formattedPhoneNumber,
+ isFirstRemovableSubscription,
+ isDefaultSubscriptionSelection,
+ isValidSubscription,
+ isUsableSubscription,
+ isActiveSubscriptionId,
+ isAvailableSubscription,
+ isActiveDataSubscriptionId);
}
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index c058a61..f22e090 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -19,6 +19,9 @@
import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
+import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_GO_TO_APP;
+
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
import static com.google.common.truth.Truth.assertThat;
@@ -32,6 +35,7 @@
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.media.NearbyDevice;
+import android.media.RouteListingPreference;
import android.os.Parcel;
import com.android.settingslib.bluetooth.A2dpProfile;
@@ -110,6 +114,8 @@
@Mock
private MediaRouter2Manager mMediaRouter2Manager;
+ private RouteListingPreference.Item mItem;
+
private BluetoothMediaDevice mBluetoothMediaDevice1;
private BluetoothMediaDevice mBluetoothMediaDevice2;
private BluetoothMediaDevice mBluetoothMediaDevice3;
@@ -497,4 +503,21 @@
assertThat(mBluetoothMediaDevice1.getFeatures().size()).isEqualTo(0);
}
+
+ @Test
+ public void getSelectionBehavior_setItemWithSelectionBehaviorOnSystemRoute_returnTransfer() {
+ mItem = new RouteListingPreference.Item.Builder(DEVICE_ADDRESS_1)
+ .setSelectionBehavior(SELECTION_BEHAVIOR_GO_TO_APP)
+ .build();
+ mBluetoothMediaDevice1 = new BluetoothMediaDevice(mContext, mCachedDevice1,
+ mMediaRouter2Manager, null /* MediaRoute2Info */, TEST_PACKAGE_NAME, mItem);
+ mPhoneMediaDevice =
+ new PhoneMediaDevice(mContext, mMediaRouter2Manager, mPhoneRouteInfo,
+ TEST_PACKAGE_NAME, mItem);
+
+ assertThat(mBluetoothMediaDevice1.getSelectionBehavior()).isEqualTo(
+ SELECTION_BEHAVIOR_TRANSFER);
+ assertThat(mPhoneMediaDevice.getSelectionBehavior()).isEqualTo(
+ SELECTION_BEHAVIOR_TRANSFER);
+ }
}
diff --git a/packages/SystemUI/compose/features/Android.bp b/packages/SystemUI/compose/features/Android.bp
index 8f8bb1b..c6438c9 100644
--- a/packages/SystemUI/compose/features/Android.bp
+++ b/packages/SystemUI/compose/features/Android.bp
@@ -34,6 +34,7 @@
"PlatformComposeCore",
"androidx.compose.runtime_runtime",
+ "androidx.compose.animation_animation-graphics",
"androidx.compose.material3_material3",
"androidx.activity_activity-compose",
],
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 323fed0..85178bc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalAnimationApi::class)
+@file:OptIn(ExperimentalAnimationApi::class, ExperimentalAnimationGraphicsApi::class)
package com.android.systemui.bouncer.ui.composable
@@ -29,11 +29,14 @@
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.core.keyframes
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
-import androidx.compose.foundation.Canvas
+import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
+import androidx.compose.animation.graphics.res.animatedVectorResource
+import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
+import androidx.compose.animation.graphics.vector.AnimatedImageVector
+import androidx.compose.foundation.Image
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -61,8 +64,10 @@
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.Constraints
@@ -70,6 +75,7 @@
import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings
import com.android.compose.grid.VerticalGrid
+import com.android.internal.R.id.image
import com.android.systemui.R
import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance
import com.android.systemui.bouncer.ui.viewmodel.EnteredKey
@@ -139,7 +145,8 @@
else -> EntryVisibility.Hidden
}
- ObscuredInputEntry(updateTransition(visibility, label = "Pin Entry $entry"))
+ val shape = viewModel.pinShapes.getShape(entry.sequenceNumber)
+ PinInputEntry(shape, updateTransition(visibility, label = "Pin Entry $entry"))
LaunchedEffect(entry) {
// Remove entry from visiblePinEntries once the hide transition completed.
@@ -171,15 +178,11 @@
}
@Composable
-private fun ObscuredInputEntry(transition: Transition<EntryVisibility>) {
+private fun PinInputEntry(shapeResourceId: Int, transition: Transition<EntryVisibility>) {
// spec: http://shortn/_DEhE3Xl2bi
- val shapePadding = 6.dp
- val shapeOvershootSize = 22.dp
val dismissStaggerDelayMs = 33
val dismissDurationMs = 450
val expansionDurationMs = 250
- val shapeExpandDurationMs = 83
- val shapeRetractDurationMs = 167
val shapeCollapseDurationMs = 200
val animatedEntryWidth by
@@ -194,19 +197,17 @@
},
label = "entry space"
) { state ->
- if (state == EntryVisibility.Shown) entryShapeSize + (shapePadding * 2) else 0.dp
+ if (state == EntryVisibility.Shown) entryShapeSize else 0.dp
}
val animatedShapeSize by
transition.animateDp(
transitionSpec = {
when {
- EntryVisibility.Hidden isTransitioningTo EntryVisibility.Shown ->
- keyframes {
- durationMillis = shapeExpandDurationMs + shapeRetractDurationMs
- 0.dp at 0 with Easings.Linear
- shapeOvershootSize at shapeExpandDurationMs with Easings.Legacy
- }
+ EntryVisibility.Hidden isTransitioningTo EntryVisibility.Shown -> {
+ // The AVD contains the entry transition.
+ snap()
+ }
targetState is EntryVisibility.BulkHidden -> {
val target = targetState as EntryVisibility.BulkHidden
tween(
@@ -220,17 +221,21 @@
},
label = "shape size"
) { state ->
- when (state) {
- EntryVisibility.Shown -> entryShapeSize
- else -> 0.dp
- }
+ if (state == EntryVisibility.Shown) entryShapeSize else 0.dp
}
val dotColor = MaterialTheme.colorScheme.onSurfaceVariant
Layout(
content = {
- // TODO(b/282730134): add support for dot shapes.
- Canvas(Modifier) { drawCircle(dotColor) }
+ val image = AnimatedImageVector.animatedVectorResource(shapeResourceId)
+ var atEnd by remember { mutableStateOf(false) }
+ Image(
+ painter = rememberAnimatedVectorPainter(image, atEnd),
+ contentDescription = null,
+ contentScale = ContentScale.Crop,
+ colorFilter = ColorFilter.tint(dotColor),
+ )
+ LaunchedEffect(Unit) { atEnd = true }
}
) { measurables, _ ->
val shapeSizePx = animatedShapeSize.roundToPx()
@@ -507,7 +512,7 @@
}
}
-private val entryShapeSize = 16.dp
+private val entryShapeSize = 30.dp
private val pinButtonSize = 84.dp
private val pinButtonErrorShrinkFactor = 67.dp / pinButtonSize
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index d208404..b9d6643 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -33,8 +33,8 @@
import com.android.systemui.animation.GlyphCallback
import com.android.systemui.animation.TextAnimator
import com.android.systemui.customization.R
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.core.MessageBuffer
import java.io.PrintWriter
import java.util.Calendar
import java.util.Locale
@@ -51,7 +51,12 @@
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : TextView(context, attrs, defStyleAttr, defStyleRes) {
- var logBuffer: LogBuffer? = null
+ var messageBuffer: MessageBuffer? = null
+ set(value) {
+ logger = if (value != null) Logger(value, TAG) else null
+ }
+
+ private var logger: Logger? = null
private val time = Calendar.getInstance()
@@ -129,7 +134,7 @@
override fun onAttachedToWindow() {
super.onAttachedToWindow()
- logBuffer?.log(TAG, DEBUG, "onAttachedToWindow")
+ logger?.d("onAttachedToWindow")
refreshFormat()
}
@@ -145,39 +150,32 @@
time.timeInMillis = timeOverrideInMillis ?: System.currentTimeMillis()
contentDescription = DateFormat.format(descFormat, time)
val formattedText = DateFormat.format(format, time)
- logBuffer?.log(TAG, DEBUG,
- { str1 = formattedText?.toString() },
- { "refreshTime: new formattedText=$str1" }
- )
+ logger?.d({ "refreshTime: new formattedText=$str1" }) { str1 = formattedText?.toString() }
// Setting text actually triggers a layout pass (because the text view is set to
// wrap_content width and TextView always relayouts for this). Avoid needless
// relayout if the text didn't actually change.
if (!TextUtils.equals(text, formattedText)) {
text = formattedText
- logBuffer?.log(TAG, DEBUG,
- { str1 = formattedText?.toString() },
- { "refreshTime: done setting new time text to: $str1" }
- )
+ logger?.d({ "refreshTime: done setting new time text to: $str1" }) {
+ str1 = formattedText?.toString()
+ }
// Because the TextLayout may mutate under the hood as a result of the new text, we
// notify the TextAnimator that it may have changed and request a measure/layout. A
// crash will occur on the next invocation of setTextStyle if the layout is mutated
// without being notified TextInterpolator being notified.
if (layout != null) {
textAnimator?.updateLayout(layout)
- logBuffer?.log(TAG, DEBUG, "refreshTime: done updating textAnimator layout")
+ logger?.d("refreshTime: done updating textAnimator layout")
}
requestLayout()
- logBuffer?.log(TAG, DEBUG, "refreshTime: after requestLayout")
+ logger?.d("refreshTime: after requestLayout")
}
}
fun onTimeZoneChanged(timeZone: TimeZone?) {
time.timeZone = timeZone
refreshFormat()
- logBuffer?.log(TAG, DEBUG,
- { str1 = timeZone?.toString() },
- { "onTimeZoneChanged newTimeZone=$str1" }
- )
+ logger?.d({ "onTimeZoneChanged newTimeZone=$str1" }) { str1 = timeZone?.toString() }
}
@SuppressLint("DrawAllocation")
@@ -191,7 +189,7 @@
} else {
animator.updateLayout(layout)
}
- logBuffer?.log(TAG, DEBUG, "onMeasure")
+ logger?.d("onMeasure")
}
override fun onDraw(canvas: Canvas) {
@@ -203,12 +201,12 @@
} else {
super.onDraw(canvas)
}
- logBuffer?.log(TAG, DEBUG, "onDraw")
+ logger?.d("onDraw")
}
override fun invalidate() {
super.invalidate()
- logBuffer?.log(TAG, DEBUG, "invalidate")
+ logger?.d("invalidate")
}
override fun onTextChanged(
@@ -218,10 +216,7 @@
lengthAfter: Int
) {
super.onTextChanged(text, start, lengthBefore, lengthAfter)
- logBuffer?.log(TAG, DEBUG,
- { str1 = text.toString() },
- { "onTextChanged text=$str1" }
- )
+ logger?.d({ "onTextChanged text=$str1" }) { str1 = text.toString() }
}
fun setLineSpacingScale(scale: Float) {
@@ -235,7 +230,7 @@
}
fun animateColorChange() {
- logBuffer?.log(TAG, DEBUG, "animateColorChange")
+ logger?.d("animateColorChange")
setTextStyle(
weight = lockScreenWeight,
textSize = -1f,
@@ -257,7 +252,7 @@
}
fun animateAppearOnLockscreen() {
- logBuffer?.log(TAG, DEBUG, "animateAppearOnLockscreen")
+ logger?.d("animateAppearOnLockscreen")
setTextStyle(
weight = dozingWeight,
textSize = -1f,
@@ -283,7 +278,7 @@
if (isAnimationEnabled && textAnimator == null) {
return
}
- logBuffer?.log(TAG, DEBUG, "animateFoldAppear")
+ logger?.d("animateFoldAppear")
setTextStyle(
weight = lockScreenWeightInternal,
textSize = -1f,
@@ -310,7 +305,7 @@
// Skip charge animation if dozing animation is already playing.
return
}
- logBuffer?.log(TAG, DEBUG, "animateCharge")
+ logger?.d("animateCharge")
val startAnimPhase2 = Runnable {
setTextStyle(
weight = if (isDozing()) dozingWeight else lockScreenWeight,
@@ -334,7 +329,7 @@
}
fun animateDoze(isDozing: Boolean, animate: Boolean) {
- logBuffer?.log(TAG, DEBUG, "animateDoze")
+ logger?.d("animateDoze")
setTextStyle(
weight = if (isDozing) dozingWeight else lockScreenWeight,
textSize = -1f,
@@ -453,10 +448,7 @@
isSingleLineInternal && !use24HourFormat -> Patterns.sClockView12
else -> DOUBLE_LINE_FORMAT_12_HOUR
}
- logBuffer?.log(TAG, DEBUG,
- { str1 = format?.toString() },
- { "refreshFormat format=$str1" }
- )
+ logger?.d({ "refreshFormat format=$str1" }) { str1 = format?.toString() }
descFormat = if (use24HourFormat) Patterns.sClockView24 else Patterns.sClockView12
refreshTime()
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 12f7452..d65edae 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
@@ -23,10 +23,11 @@
import android.provider.Settings
import android.util.Log
import androidx.annotation.OpenForTesting
-import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogMessageImpl
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.core.LogMessage
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.log.core.MessageInitializer
import com.android.systemui.log.core.MessagePrinter
import com.android.systemui.plugins.ClockController
@@ -75,7 +76,7 @@
private val TMP_MESSAGE: LogMessage by lazy { LogMessageImpl.Factory.create() }
-private inline fun LogBuffer?.tryLog(
+private inline fun Logger?.tryLog(
tag: String,
level: LogLevel,
messageInitializer: MessageInitializer,
@@ -84,7 +85,7 @@
) {
if (this != null) {
// Wrap messagePrinter to convert it from crossinline to noinline
- this.log(tag, level, messageInitializer, messagePrinter, ex)
+ this.log(level, messagePrinter, ex, messageInitializer)
} else {
messageInitializer(TMP_MESSAGE)
val msg = messagePrinter(TMP_MESSAGE)
@@ -110,7 +111,7 @@
val handleAllUsers: Boolean,
defaultClockProvider: ClockProvider,
val fallbackClockId: ClockId = DEFAULT_CLOCK_ID,
- val logBuffer: LogBuffer? = null,
+ messageBuffer: MessageBuffer? = null,
val keepAllLoaded: Boolean,
subTag: String,
var isTransitClockEnabled: Boolean = false,
@@ -124,6 +125,7 @@
fun onAvailableClocksChanged() {}
}
+ private val logger: Logger? = if (messageBuffer != null) Logger(messageBuffer, TAG) else null
private val availableClocks = ConcurrentHashMap<ClockId, ClockInfo>()
private val clockChangeListeners = mutableListOf<ClockChangeListener>()
private val settingObserver =
@@ -150,7 +152,7 @@
val knownClocks = KNOWN_PLUGINS.get(manager.getPackage())
if (knownClocks == null) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.WARNING,
{ str1 = manager.getPackage() },
@@ -159,7 +161,7 @@
return true
}
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.INFO,
{ str1 = manager.getPackage() },
@@ -176,7 +178,7 @@
}
if (manager != info.manager) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.ERROR,
{ str1 = id },
@@ -216,7 +218,7 @@
}
if (manager != info.manager) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.ERROR,
{ str1 = id },
@@ -244,7 +246,7 @@
val id = clock.clockId
val info = availableClocks[id]
if (info?.manager != manager) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.ERROR,
{ str1 = id },
@@ -319,7 +321,7 @@
ClockSettings.deserialize(json)
} catch (ex: Exception) {
- logBuffer.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to parse clock settings" }, ex)
+ logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to parse clock settings" }, ex)
null
}
settings = result
@@ -348,7 +350,7 @@
)
}
} catch (ex: Exception) {
- logBuffer.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to set clock settings" }, ex)
+ logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to set clock settings" }, ex)
}
settings = value
}
@@ -508,9 +510,9 @@
}
private fun onConnected(clockId: ClockId) {
- logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Connected $str1" })
+ logger.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Connected $str1" })
if (currentClockId == clockId) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.INFO,
{ str1 = clockId },
@@ -520,10 +522,10 @@
}
private fun onLoaded(clockId: ClockId) {
- logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Loaded $str1" })
+ logger.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Loaded $str1" })
if (currentClockId == clockId) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.INFO,
{ str1 = clockId },
@@ -534,10 +536,10 @@
}
private fun onUnloaded(clockId: ClockId) {
- logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Unloaded $str1" })
+ logger.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Unloaded $str1" })
if (currentClockId == clockId) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.WARNING,
{ str1 = clockId },
@@ -548,10 +550,10 @@
}
private fun onDisconnected(clockId: ClockId) {
- logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Disconnected $str1" })
+ logger.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Disconnected $str1" })
if (currentClockId == clockId) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.WARNING,
{ str1 = clockId },
@@ -597,22 +599,17 @@
if (isEnabled && clockId.isNotEmpty()) {
val clock = createClock(clockId)
if (clock != null) {
- logBuffer.tryLog(
- TAG,
- LogLevel.INFO,
- { str1 = clockId },
- { "Rendering clock $str1" }
- )
+ logger.tryLog(TAG, LogLevel.INFO, { str1 = clockId }, { "Rendering clock $str1" })
return clock
} else if (availableClocks.containsKey(clockId)) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.WARNING,
{ str1 = clockId },
{ "Clock $str1 not loaded; using default" }
)
} else {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.ERROR,
{ str1 = clockId },
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index e557c8e..e539c95 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -24,7 +24,7 @@
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import com.android.systemui.customization.R
-import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.ClockAnimations
import com.android.systemui.plugins.ClockConfig
import com.android.systemui.plugins.ClockController
@@ -108,10 +108,10 @@
override val config = ClockFaceConfig()
- override var logBuffer: LogBuffer?
- get() = view.logBuffer
+ override var messageBuffer: MessageBuffer?
+ get() = view.messageBuffer
set(value) {
- view.logBuffer = value
+ view.messageBuffer = value
}
override var animations: DefaultClockAnimations = DefaultClockAnimations(view, 0f, 0f)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 537b7a4..d962732 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -18,7 +18,7 @@
import android.graphics.drawable.Drawable
import android.view.View
import com.android.internal.annotations.Keep
-import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.annotations.ProvidesInterface
import java.io.PrintWriter
import java.util.Locale
@@ -95,7 +95,7 @@
val animations: ClockAnimations
/** Some clocks may log debug information */
- var logBuffer: LogBuffer?
+ var messageBuffer: MessageBuffer?
}
/** Events that should call when various rendering parameters change */
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index 02d5510..3194815 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -62,15 +62,12 @@
-keep class ** extends androidx.preference.PreferenceFragment
-keep class com.android.systemui.tuner.*
-# The plugins, log & common subpackages act as shared libraries that might be referenced in
+# The plugins and core log subpackages act as shared libraries that might be referenced in
# dynamically-loaded plugin APKs.
-keep class com.android.systemui.plugins.** {
*;
}
--keep class com.android.systemui.log.** {
- *;
-}
--keep class com.android.systemui.common.** {
+-keep class com.android.systemui.log.core.** {
*;
}
-keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
diff --git a/packages/SystemUI/res/drawable/pin_dot_avd.xml b/packages/SystemUI/res/drawable/pin_dot_avd.xml
index 1c16251..710ba83 100644
--- a/packages/SystemUI/res/drawable/pin_dot_avd.xml
+++ b/packages/SystemUI/res/drawable/pin_dot_avd.xml
@@ -1 +1,40 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="30dp" android:width="30dp" android:viewportHeight="30" android:viewportWidth="30"><group android:name="_R_G"><group android:name="_R_G_L_0_G" android:translateX="28.237000000000002" android:translateY="23.112000000000002"><path android:name="_R_G_L_0_G_D_0_P_0" android:strokeColor="#ffffff" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M-13.24 -12.11 C-11.03,-12.11 -9.24,-10.32 -9.24,-8.11 C-9.24,-5.9 -11.03,-4.11 -13.24,-4.11 C-15.44,-4.11 -17.24,-5.9 -17.24,-8.11 C-17.24,-10.32 -15.44,-12.11 -13.24,-12.11c "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="500" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:height="30dp"
+ android:width="30dp"
+ android:viewportHeight="30"
+ android:viewportWidth="30">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="28.237000000000002"
+ android:translateY="23.112000000000002">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:strokeColor="#ffffff"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2"
+ android:strokeAlpha="1"
+ android:pathData=" M-13.24 -12.11 C-11.03,-12.11 -9.24,-10.32 -9.24,-8.11 C-9.24,-5.9 -11.03,-4.11 -13.24,-4.11 C-15.44,-4.11 -17.24,-5.9 -17.24,-8.11 C-17.24,-10.32 -15.44,-12.11 -13.24,-12.11c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="translateX"
+ android:duration="500"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/pin_dot_delete_avd.xml b/packages/SystemUI/res/drawable/pin_dot_delete_avd.xml
index 0f8703f..72a03bf 100644
--- a/packages/SystemUI/res/drawable/pin_dot_delete_avd.xml
+++ b/packages/SystemUI/res/drawable/pin_dot_delete_avd.xml
@@ -1 +1,130 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="30dp" android:width="30dp" android:viewportHeight="30" android:viewportWidth="30"><group android:name="_R_G"><group android:name="_R_G_L_0_G" android:translateX="28.54" android:translateY="23.54"><path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-13.54 -16.54 C-9.12,-16.54 -5.54,-12.96 -5.54,-8.54 C-5.54,-4.12 -9.12,-0.54 -13.54,-0.54 C-17.96,-0.54 -21.54,-4.12 -21.54,-8.54 C-21.54,-12.96 -17.96,-16.54 -13.54,-16.54c "/><path android:name="_R_G_L_0_G_D_1_P_0" android:strokeColor="#ffffff" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="0" android:strokeAlpha="1" android:pathData=" M-13.54 -16.54 C-9.12,-16.54 -5.54,-12.96 -5.54,-8.54 C-5.54,-4.12 -9.12,-0.54 -13.54,-0.54 C-17.96,-0.54 -21.54,-4.12 -21.54,-8.54 C-21.54,-12.96 -17.96,-16.54 -13.54,-16.54c "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="fillAlpha" android:duration="150" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="fillAlpha" android:duration="200" android:startOffset="150" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="150" android:startOffset="0" android:valueFrom="M-13.54 -16.54 C-9.12,-16.54 -5.54,-12.96 -5.54,-8.54 C-5.54,-4.12 -9.12,-0.54 -13.54,-0.54 C-17.96,-0.54 -21.54,-4.12 -21.54,-8.54 C-21.54,-12.96 -17.96,-16.54 -13.54,-16.54c " android:valueTo="M-13.54 -11.54 C-11.88,-11.54 -10.54,-10.2 -10.54,-8.54 C-10.54,-6.88 -11.88,-5.54 -13.54,-5.54 C-15.2,-5.54 -16.54,-6.88 -16.54,-8.54 C-16.54,-10.2 -15.2,-11.54 -13.54,-11.54c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="200" android:startOffset="150" android:valueFrom="M-13.54 -11.54 C-11.88,-11.54 -10.54,-10.2 -10.54,-8.54 C-10.54,-6.88 -11.88,-5.54 -13.54,-5.54 C-15.2,-5.54 -16.54,-6.88 -16.54,-8.54 C-16.54,-10.2 -15.2,-11.54 -13.54,-11.54c " android:valueTo="M-13.54 -12.54 C-11.33,-12.54 -9.54,-10.75 -9.54,-8.54 C-9.54,-6.33 -11.33,-4.54 -13.54,-4.54 C-15.75,-4.54 -17.54,-6.33 -17.54,-8.54 C-17.54,-10.75 -15.75,-12.54 -13.54,-12.54c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeWidth" android:duration="150" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="strokeWidth" android:duration="50" android:startOffset="150" android:valueFrom="0" android:valueTo="2" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="150" android:startOffset="0" android:valueFrom="M-13.54 -16.54 C-9.12,-16.54 -5.54,-12.96 -5.54,-8.54 C-5.54,-4.12 -9.12,-0.54 -13.54,-0.54 C-17.96,-0.54 -21.54,-4.12 -21.54,-8.54 C-21.54,-12.96 -17.96,-16.54 -13.54,-16.54c " android:valueTo="M-13.54 -11.54 C-11.88,-11.54 -10.54,-10.2 -10.54,-8.54 C-10.54,-6.88 -11.88,-5.54 -13.54,-5.54 C-15.2,-5.54 -16.54,-6.88 -16.54,-8.54 C-16.54,-10.2 -15.2,-11.54 -13.54,-11.54c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="200" android:startOffset="150" android:valueFrom="M-13.54 -11.54 C-11.88,-11.54 -10.54,-10.2 -10.54,-8.54 C-10.54,-6.88 -11.88,-5.54 -13.54,-5.54 C-15.2,-5.54 -16.54,-6.88 -16.54,-8.54 C-16.54,-10.2 -15.2,-11.54 -13.54,-11.54c " android:valueTo="M-13.54 -12.54 C-11.33,-12.54 -9.54,-10.75 -9.54,-8.54 C-9.54,-6.33 -11.33,-4.54 -13.54,-4.54 C-15.75,-4.54 -17.54,-6.33 -17.54,-8.54 C-17.54,-10.75 -15.75,-12.54 -13.54,-12.54c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="500" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="150"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.43"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="150"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.43"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="fillAlpha"
+ android:duration="200"
+ android:startOffset="150"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleX"
+ android:startOffset="150"
+ android:valueFrom="0.65"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleY"
+ android:startOffset="150"
+ android:valueFrom="0.65"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="500"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="30dp"
+ android:height="30dp"
+ android:viewportHeight="30"
+ android:viewportWidth="30">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:pivotX="-13.54"
+ android:pivotY="-8.54"
+ android:scaleX="1"
+ android:scaleY="1"
+ android:translateX="28.54"
+ android:translateY="23.54">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-13.54 -16.54 C-9.12,-16.54 -5.54,-12.96 -5.54,-8.54 C-5.54,-4.12 -9.12,-0.54 -13.54,-0.54 C-17.96,-0.54 -21.54,-4.12 -21.54,-8.54 C-21.54,-12.96 -17.96,-16.54 -13.54,-16.54c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:pivotX="317.509"
+ android:pivotY="-826.986"
+ android:scaleX="0.65"
+ android:scaleY="0.65"
+ android:translateX="-302.509"
+ android:translateY="841.986">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M317.51 -821.99 C314.75,-821.99 312.51,-824.23 312.51,-826.99 C312.51,-829.74 314.75,-831.99 317.51,-831.99 C320.27,-831.99 322.51,-829.74 322.51,-826.99 C322.51,-824.23 320.27,-821.99 317.51,-821.99z M317.51 -829.99 C315.86,-829.99 314.51,-828.64 314.51,-826.99 C314.51,-825.33 315.86,-823.99 317.51,-823.99 C319.16,-823.99 320.51,-825.33 320.51,-826.99 C320.51,-828.64 319.16,-829.99 317.51,-829.99z " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/pin_dot_shape_1_avd.xml b/packages/SystemUI/res/drawable/pin_dot_shape_1_avd.xml
index f1fb2aa..02abb99 100644
--- a/packages/SystemUI/res/drawable/pin_dot_shape_1_avd.xml
+++ b/packages/SystemUI/res/drawable/pin_dot_shape_1_avd.xml
@@ -1 +1,141 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="30dp" android:width="30dp" android:viewportHeight="30" android:viewportWidth="30"><group android:name="_R_G"><group android:name="_R_G_L_1_G" android:translateX="15.441" android:translateY="15.691" android:scaleY="0"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-0.44 -12.06 C13.5,-14.63 13.5,-14.63 10.9,-0.69 C13.5,13.25 13.5,13.25 -0.44,10.53 C-14.38,13.25 -14.38,13.25 -11.86,-0.69 C-14.38,-14.63 -14.38,-14.63 -0.44,-12.06c "/></group><group android:name="_R_G_L_0_G" android:translateX="15" android:translateY="15" android:scaleX="0" android:scaleY="0"><path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-13.65 -3.95 C-16.31,-10.09 -10.09,-16.31 -3.95,-13.65 C-3.95,-13.65 -2.94,-13.21 -2.94,-13.21 C-1.06,-12.39 1.06,-12.39 2.94,-13.21 C2.94,-13.21 3.95,-13.65 3.95,-13.65 C10.09,-16.31 16.31,-10.09 13.65,-3.95 C13.65,-3.95 13.21,-2.94 13.21,-2.94 C12.39,-1.06 12.39,1.06 13.21,2.94 C13.21,2.94 13.65,3.95 13.65,3.95 C16.31,10.09 10.09,16.31 3.95,13.65 C3.95,13.65 2.94,13.21 2.94,13.21 C1.06,12.39 -1.06,12.39 -2.94,13.21 C-2.94,13.21 -3.95,13.65 -3.95,13.65 C-10.09,16.31 -16.31,10.09 -13.65,3.95 C-13.65,3.95 -13.21,2.94 -13.21,2.94 C-12.39,1.06 -12.39,-1.06 -13.21,-2.94 C-13.21,-2.94 -13.65,-3.95 -13.65,-3.95c "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_1_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="67" android:startOffset="0" android:valueFrom="M-0.44 -12.06 C13.5,-14.63 13.5,-14.63 10.9,-0.69 C13.5,13.25 13.5,13.25 -0.44,10.53 C-14.38,13.25 -14.38,13.25 -11.86,-0.69 C-14.38,-14.63 -14.38,-14.63 -0.44,-12.06c " android:valueTo="M-0.44 -12.06 C13.5,-14.63 13.5,-14.63 10.9,-0.69 C13.5,13.25 13.5,13.25 -0.44,10.53 C-14.38,13.25 -14.38,13.25 -11.86,-0.69 C-14.38,-14.63 -14.38,-14.63 -0.44,-12.06c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="283" android:startOffset="67" android:valueFrom="M-0.44 -12.06 C13.5,-14.63 13.5,-14.63 10.9,-0.69 C13.5,13.25 13.5,13.25 -0.44,10.53 C-14.38,13.25 -14.38,13.25 -11.86,-0.69 C-14.38,-14.63 -14.38,-14.63 -0.44,-12.06c " android:valueTo="M-0.44 -8.69 C3.98,-8.69 7.56,-5.11 7.56,-0.69 C7.56,3.73 3.98,7.31 -0.44,7.31 C-4.86,7.31 -8.44,3.73 -8.44,-0.69 C-8.44,-5.11 -4.86,-8.69 -0.44,-8.69c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="283" android:startOffset="67" android:valueFrom="1" android:valueTo="0.3" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="283" android:startOffset="67" android:valueFrom="1" android:valueTo="0.3" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="367" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"/></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="500" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-0.44 -12.06 C13.5,-14.63 13.5,-14.63 10.9,-0.69 C13.5,13.25 13.5,13.25 -0.44,10.53 C-14.38,13.25 -14.38,13.25 -11.86,-0.69 C-14.38,-14.63 -14.38,-14.63 -0.44,-12.06c "
+ android:valueTo="M-0.44 -12.06 C13.5,-14.63 13.5,-14.63 10.9,-0.69 C13.5,13.25 13.5,13.25 -0.44,10.53 C-14.38,13.25 -14.38,13.25 -11.86,-0.69 C-14.38,-14.63 -14.38,-14.63 -0.44,-12.06c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="283"
+ android:propertyName="pathData"
+ android:startOffset="67"
+ android:valueFrom="M-0.44 -12.06 C13.5,-14.63 13.5,-14.63 10.9,-0.69 C13.5,13.25 13.5,13.25 -0.44,10.53 C-14.38,13.25 -14.38,13.25 -11.86,-0.69 C-14.38,-14.63 -14.38,-14.63 -0.44,-12.06c "
+ android:valueTo="M-0.44 -8.69 C3.98,-8.69 7.56,-5.11 7.56,-0.69 C7.56,3.73 3.98,7.31 -0.44,7.31 C-4.86,7.31 -8.44,3.73 -8.44,-0.69 C-8.44,-5.11 -4.86,-8.69 -0.44,-8.69c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="0"
+ android:propertyName="scaleY"
+ android:startOffset="67"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="283"
+ android:propertyName="scaleX"
+ android:startOffset="67"
+ android:valueFrom="1"
+ android:valueTo="0.3"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.6,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="283"
+ android:propertyName="scaleY"
+ android:startOffset="67"
+ android:valueFrom="1"
+ android:valueTo="0.3"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.6,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="500"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="30dp"
+ android:height="30dp"
+ android:viewportHeight="30"
+ android:viewportWidth="30">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:scaleY="0"
+ android:translateX="15.441"
+ android:translateY="15.691">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-0.44 -12.06 C13.5,-14.63 13.5,-14.63 10.9,-0.69 C13.5,13.25 13.5,13.25 -0.44,10.53 C-14.38,13.25 -14.38,13.25 -11.86,-0.69 C-14.38,-14.63 -14.38,-14.63 -0.44,-12.06c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="15"
+ android:translateY="15">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-13.65 -3.95 C-16.31,-10.09 -10.09,-16.31 -3.95,-13.65 C-3.95,-13.65 -2.94,-13.21 -2.94,-13.21 C-1.06,-12.39 1.06,-12.39 2.94,-13.21 C2.94,-13.21 3.95,-13.65 3.95,-13.65 C10.09,-16.31 16.31,-10.09 13.65,-3.95 C13.65,-3.95 13.21,-2.94 13.21,-2.94 C12.39,-1.06 12.39,1.06 13.21,2.94 C13.21,2.94 13.65,3.95 13.65,3.95 C16.31,10.09 10.09,16.31 3.95,13.65 C3.95,13.65 2.94,13.21 2.94,13.21 C1.06,12.39 -1.06,12.39 -2.94,13.21 C-2.94,13.21 -3.95,13.65 -3.95,13.65 C-10.09,16.31 -16.31,10.09 -13.65,3.95 C-13.65,3.95 -13.21,2.94 -13.21,2.94 C-12.39,1.06 -12.39,-1.06 -13.21,-2.94 C-13.21,-2.94 -13.65,-3.95 -13.65,-3.95c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/pin_dot_shape_2_avd.xml b/packages/SystemUI/res/drawable/pin_dot_shape_2_avd.xml
index 3717db8..1607327 100644
--- a/packages/SystemUI/res/drawable/pin_dot_shape_2_avd.xml
+++ b/packages/SystemUI/res/drawable/pin_dot_shape_2_avd.xml
@@ -1 +1,148 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="30dp" android:width="30dp" android:viewportHeight="30" android:viewportWidth="30"><group android:name="_R_G"><group android:name="_R_G_L_1_G" android:translateX="15.397" android:translateY="15.691" android:scaleY="0"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-0.56 -14.03 C3.65,-13.99 14.58,7.64 11.51,10.42 C8.45,13.2 5.92,9.56 -0.46,9.61 C-6.85,9.65 -9.27,12.76 -12.33,10.46 C-15.39,8.15 -4.77,-14.07 -0.56,-14.03c "/></group><group android:name="_R_G_L_0_G" android:translateX="15" android:translateY="13.205" android:pivotY="1.795" android:scaleX="0" android:scaleY="0"><path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M12.78 7.57 C12.78,7.57 4.72,-8.55 4.72,-8.55 C2.77,-12.44 -2.77,-12.44 -4.72,-8.55 C-4.72,-8.55 -12.78,7.57 -12.78,7.57 C-15,12.01 -10.42,16.78 -5.89,14.74 C-5.89,14.74 -2.17,13.07 -2.17,13.07 C-0.79,12.45 0.79,12.45 2.17,13.07 C2.17,13.07 5.9,14.74 5.9,14.74 C10.42,16.78 15,12.01 12.78,7.57c "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_1_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="67" android:startOffset="0" android:valueFrom="M-0.56 -14.03 C3.65,-13.99 14.58,7.64 11.51,10.42 C8.45,13.2 5.92,9.56 -0.46,9.61 C-6.85,9.65 -9.27,12.76 -12.33,10.46 C-15.39,8.15 -4.77,-14.07 -0.56,-14.03c " android:valueTo="M-0.56 -14.03 C3.65,-13.99 14.58,7.64 11.51,10.42 C8.45,13.2 5.92,9.56 -0.46,9.61 C-6.85,9.65 -9.27,12.76 -12.33,10.46 C-15.39,8.15 -4.77,-14.07 -0.56,-14.03c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="283" android:startOffset="67" android:valueFrom="M-0.56 -14.03 C3.65,-13.99 14.58,7.64 11.51,10.42 C8.45,13.2 5.92,9.56 -0.46,9.61 C-6.85,9.65 -9.27,12.76 -12.33,10.46 C-15.39,8.15 -4.77,-14.07 -0.56,-14.03c " android:valueTo="M-0.44 -8.69 C3.98,-8.69 7.56,-5.11 7.56,-0.69 C7.56,3.73 3.98,7.31 -0.44,7.31 C-4.86,7.31 -8.44,3.73 -8.44,-0.69 C-8.44,-5.11 -4.86,-8.69 -0.44,-8.69c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="67" android:startOffset="0" android:valueFrom="M12.78 7.57 C12.78,7.57 4.72,-8.55 4.72,-8.55 C2.77,-12.44 -2.77,-12.44 -4.72,-8.55 C-4.72,-8.55 -12.78,7.57 -12.78,7.57 C-15,12.01 -10.42,16.78 -5.89,14.74 C-5.89,14.74 -2.17,13.07 -2.17,13.07 C-0.79,12.45 0.79,12.45 2.17,13.07 C2.17,13.07 5.9,14.74 5.9,14.74 C10.42,16.78 15,12.01 12.78,7.57c " android:valueTo="M12.78 7.57 C12.78,7.57 4.72,-8.55 4.72,-8.55 C2.77,-12.44 -2.77,-12.44 -4.72,-8.55 C-4.72,-8.55 -12.78,7.57 -12.78,7.57 C-15,12.01 -10.42,16.78 -5.89,14.74 C-5.89,14.74 -2.17,13.07 -2.17,13.07 C-0.79,12.45 0.79,12.45 2.17,13.07 C2.17,13.07 5.9,14.74 5.9,14.74 C10.42,16.78 15,12.01 12.78,7.57c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.833,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="283" android:startOffset="67" android:valueFrom="M12.78 7.57 C12.78,7.57 4.72,-8.55 4.72,-8.55 C2.77,-12.44 -2.77,-12.44 -4.72,-8.55 C-4.72,-8.55 -12.78,7.57 -12.78,7.57 C-15,12.01 -10.42,16.78 -5.89,14.74 C-5.89,14.74 -2.17,13.07 -2.17,13.07 C-0.79,12.45 0.79,12.45 2.17,13.07 C2.17,13.07 5.9,14.74 5.9,14.74 C10.42,16.78 15,12.01 12.78,7.57c " android:valueTo="M6.12 0.21 C6.12,0.21 2.27,-4.35 2.27,-4.35 C1.34,-6.2 -1.3,-6.2 -2.23,-4.35 C-2.23,-4.35 -6.06,0.21 -6.06,0.21 C-7.12,2.33 -5.46,5.79 -4.03,6.54 C-2.28,7.45 -1.01,7.48 -1.01,7.48 C-1.01,7.48 1.05,7.48 1.05,7.48 C1.05,7.48 3.28,7.36 4.23,6.66 C4.92,6.15 7.18,2.33 6.12,0.21c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.833,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="367" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"/></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="500" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:height="30dp"
+ android:width="30dp"
+ android:viewportHeight="30"
+ android:viewportWidth="30">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="15.397"
+ android:translateY="15.691"
+ android:scaleY="0">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-0.56 -14.03 C3.65,-13.99 14.58,7.64 11.51,10.42 C8.45,13.2 5.92,9.56 -0.46,9.61 C-6.85,9.65 -9.27,12.76 -12.33,10.46 C-15.39,8.15 -4.77,-14.07 -0.56,-14.03c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="15"
+ android:translateY="13.205"
+ android:pivotY="1.795">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M12.78 7.57 C12.78,7.57 4.72,-8.55 4.72,-8.55 C2.77,-12.44 -2.77,-12.44 -4.72,-8.55 C-4.72,-8.55 -12.78,7.57 -12.78,7.57 C-15,12.01 -10.42,16.78 -5.89,14.74 C-5.89,14.74 -2.17,13.07 -2.17,13.07 C-0.79,12.45 0.79,12.45 2.17,13.07 C2.17,13.07 5.9,14.74 5.9,14.74 C10.42,16.78 15,12.01 12.78,7.57c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="pathData"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="M-0.56 -14.03 C3.65,-13.99 14.58,7.64 11.51,10.42 C8.45,13.2 5.92,9.56 -0.46,9.61 C-6.85,9.65 -9.27,12.76 -12.33,10.46 C-15.39,8.15 -4.77,-14.07 -0.56,-14.03c "
+ android:valueTo="M-0.56 -14.03 C3.65,-13.99 14.58,7.64 11.51,10.42 C8.45,13.2 5.92,9.56 -0.46,9.61 C-6.85,9.65 -9.27,12.76 -12.33,10.46 C-15.39,8.15 -4.77,-14.07 -0.56,-14.03c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="pathData"
+ android:duration="283"
+ android:startOffset="67"
+ android:valueFrom="M-0.56 -14.03 C3.65,-13.99 14.58,7.64 11.51,10.42 C8.45,13.2 5.92,9.56 -0.46,9.61 C-6.85,9.65 -9.27,12.76 -12.33,10.46 C-15.39,8.15 -4.77,-14.07 -0.56,-14.03c "
+ android:valueTo="M-0.44 -8.69 C3.98,-8.69 7.56,-5.11 7.56,-0.69 C7.56,3.73 3.98,7.31 -0.44,7.31 C-4.86,7.31 -8.44,3.73 -8.44,-0.69 C-8.44,-5.11 -4.86,-8.69 -0.44,-8.69c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="0"
+ android:startOffset="67"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="pathData"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="M12.78 7.57 C12.78,7.57 4.72,-8.55 4.72,-8.55 C2.77,-12.44 -2.77,-12.44 -4.72,-8.55 C-4.72,-8.55 -12.78,7.57 -12.78,7.57 C-15,12.01 -10.42,16.78 -5.89,14.74 C-5.89,14.74 -2.17,13.07 -2.17,13.07 C-0.79,12.45 0.79,12.45 2.17,13.07 C2.17,13.07 5.9,14.74 5.9,14.74 C10.42,16.78 15,12.01 12.78,7.57c "
+ android:valueTo="M12.78 7.57 C12.78,7.57 4.72,-8.55 4.72,-8.55 C2.77,-12.44 -2.77,-12.44 -4.72,-8.55 C-4.72,-8.55 -12.78,7.57 -12.78,7.57 C-15,12.01 -10.42,16.78 -5.89,14.74 C-5.89,14.74 -2.17,13.07 -2.17,13.07 C-0.79,12.45 0.79,12.45 2.17,13.07 C2.17,13.07 5.9,14.74 5.9,14.74 C10.42,16.78 15,12.01 12.78,7.57c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.833,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="pathData"
+ android:duration="283"
+ android:startOffset="67"
+ android:valueFrom="M12.78 7.57 C12.78,7.57 4.72,-8.55 4.72,-8.55 C2.77,-12.44 -2.77,-12.44 -4.72,-8.55 C-4.72,-8.55 -12.78,7.57 -12.78,7.57 C-15,12.01 -10.42,16.78 -5.89,14.74 C-5.89,14.74 -2.17,13.07 -2.17,13.07 C-0.79,12.45 0.79,12.45 2.17,13.07 C2.17,13.07 5.9,14.74 5.9,14.74 C10.42,16.78 15,12.01 12.78,7.57c "
+ android:valueTo="M6.12 0.21 C6.12,0.21 2.27,-4.35 2.27,-4.35 C1.34,-6.2 -1.3,-6.2 -2.23,-4.35 C-2.23,-4.35 -6.06,0.21 -6.06,0.21 C-7.12,2.33 -5.46,5.79 -4.03,6.54 C-2.28,7.45 -1.01,7.48 -1.01,7.48 C-1.01,7.48 1.05,7.48 1.05,7.48 C1.05,7.48 3.28,7.36 4.23,6.66 C4.92,6.15 7.18,2.33 6.12,0.21c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.833,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="scaleX"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="translateX"
+ android:duration="500"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/pin_dot_shape_3_avd.xml b/packages/SystemUI/res/drawable/pin_dot_shape_3_avd.xml
index 95b8044..78e2249 100644
--- a/packages/SystemUI/res/drawable/pin_dot_shape_3_avd.xml
+++ b/packages/SystemUI/res/drawable/pin_dot_shape_3_avd.xml
@@ -1 +1,141 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="30dp" android:width="30dp" android:viewportHeight="30" android:viewportWidth="30"><group android:name="_R_G"><group android:name="_R_G_L_1_G" android:translateX="15.441" android:translateY="15.691" android:scaleY="0"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-10.2 -11.16 C-1.58,-18.94 4.25,-12.72 8.06,-8.64 C11.88,-4.57 17.93,1.89 9.39,9.74 C0.85,17.6 -5.06,11.3 -8.87,7.22 C-12.69,3.14 -18.81,-3.39 -10.2,-11.16c "/></group><group android:name="_R_G_L_0_G" android:translateX="15" android:translateY="15" android:scaleX="0" android:scaleY="0"><path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M10.71 10.71 C5.92,15.5 -1.85,15.5 -6.64,10.71 C-6.64,10.71 -10.71,6.64 -10.71,6.64 C-15.5,1.85 -15.5,-5.92 -10.71,-10.71 C-5.92,-15.5 1.85,-15.5 6.64,-10.71 C6.64,-10.71 10.71,-6.64 10.71,-6.64 C15.5,-1.85 15.5,5.92 10.71,10.71c "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_1_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="67" android:startOffset="0" android:valueFrom="M-10.2 -11.16 C-1.58,-18.94 4.25,-12.72 8.06,-8.64 C11.88,-4.57 17.93,1.89 9.39,9.74 C0.85,17.6 -5.06,11.3 -8.87,7.22 C-12.69,3.14 -18.81,-3.39 -10.2,-11.16c " android:valueTo="M-10.2 -11.16 C-1.58,-18.94 4.25,-12.72 8.06,-8.64 C11.88,-4.57 17.93,1.89 9.39,9.74 C0.85,17.6 -5.06,11.3 -8.87,7.22 C-12.69,3.14 -18.81,-3.39 -10.2,-11.16c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="283" android:startOffset="67" android:valueFrom="M-10.2 -11.16 C-1.58,-18.94 4.25,-12.72 8.06,-8.64 C11.88,-4.57 17.93,1.89 9.39,9.74 C0.85,17.6 -5.06,11.3 -8.87,7.22 C-12.69,3.14 -18.81,-3.39 -10.2,-11.16c " android:valueTo="M-0.44 -8.69 C3.98,-8.69 7.56,-5.11 7.56,-0.69 C7.56,3.73 3.98,7.31 -0.44,7.31 C-4.86,7.31 -8.44,3.73 -8.44,-0.69 C-8.44,-5.11 -4.86,-8.69 -0.44,-8.69c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="283" android:startOffset="67" android:valueFrom="1" android:valueTo="0.4" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="283" android:startOffset="67" android:valueFrom="1" android:valueTo="0.4" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="367" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"/></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="500" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:height="30dp"
+ android:width="30dp"
+ android:viewportHeight="30"
+ android:viewportWidth="30">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="15.441"
+ android:translateY="15.691"
+ android:scaleY="0">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-10.2 -11.16 C-1.58,-18.94 4.25,-12.72 8.06,-8.64 C11.88,-4.57 17.93,1.89 9.39,9.74 C0.85,17.6 -5.06,11.3 -8.87,7.22 C-12.69,3.14 -18.81,-3.39 -10.2,-11.16c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="15"
+ android:translateY="15">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M10.71 10.71 C5.92,15.5 -1.85,15.5 -6.64,10.71 C-6.64,10.71 -10.71,6.64 -10.71,6.64 C-15.5,1.85 -15.5,-5.92 -10.71,-10.71 C-5.92,-15.5 1.85,-15.5 6.64,-10.71 C6.64,-10.71 10.71,-6.64 10.71,-6.64 C15.5,-1.85 15.5,5.92 10.71,10.71c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="pathData"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="M-10.2 -11.16 C-1.58,-18.94 4.25,-12.72 8.06,-8.64 C11.88,-4.57 17.93,1.89 9.39,9.74 C0.85,17.6 -5.06,11.3 -8.87,7.22 C-12.69,3.14 -18.81,-3.39 -10.2,-11.16c "
+ android:valueTo="M-10.2 -11.16 C-1.58,-18.94 4.25,-12.72 8.06,-8.64 C11.88,-4.57 17.93,1.89 9.39,9.74 C0.85,17.6 -5.06,11.3 -8.87,7.22 C-12.69,3.14 -18.81,-3.39 -10.2,-11.16c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="pathData"
+ android:duration="283"
+ android:startOffset="67"
+ android:valueFrom="M-10.2 -11.16 C-1.58,-18.94 4.25,-12.72 8.06,-8.64 C11.88,-4.57 17.93,1.89 9.39,9.74 C0.85,17.6 -5.06,11.3 -8.87,7.22 C-12.69,3.14 -18.81,-3.39 -10.2,-11.16c "
+ android:valueTo="M-0.44 -8.69 C3.98,-8.69 7.56,-5.11 7.56,-0.69 C7.56,3.73 3.98,7.31 -0.44,7.31 C-4.86,7.31 -8.44,3.73 -8.44,-0.69 C-8.44,-5.11 -4.86,-8.69 -0.44,-8.69c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="0"
+ android:startOffset="67"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="scaleX"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="scaleX"
+ android:duration="283"
+ android:startOffset="67"
+ android:valueFrom="1"
+ android:valueTo="0.4"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.6,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="283"
+ android:startOffset="67"
+ android:valueFrom="1"
+ android:valueTo="0.4"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.6,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="translateX"
+ android:duration="500"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/pin_dot_shape_4_avd.xml b/packages/SystemUI/res/drawable/pin_dot_shape_4_avd.xml
index 8ea8f85..35c7210 100644
--- a/packages/SystemUI/res/drawable/pin_dot_shape_4_avd.xml
+++ b/packages/SystemUI/res/drawable/pin_dot_shape_4_avd.xml
@@ -1 +1,119 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="30dp" android:width="30dp" android:viewportHeight="30" android:viewportWidth="30"><group android:name="_R_G"><group android:name="_R_G_L_1_G" android:translateX="15.441" android:translateY="15.691" android:scaleY="0"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-0.44 -8.69 C3.97,-8.69 7.56,-5.11 7.56,-0.69 C7.56,3.72 3.97,7.31 -0.44,7.31 C-4.86,7.31 -8.44,3.72 -8.44,-0.69 C-8.44,-5.11 -4.86,-8.69 -0.44,-8.69c "/></group><group android:name="_R_G_L_0_G" android:translateX="15" android:translateY="15" android:scaleX="0" android:scaleY="0"><path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M8 0 C8,-1.35 7.97,-1.94 7.41,-3.02 C6.85,-4.09 6.61,-4.79 5.66,-5.66 C4.7,-6.52 4.2,-6.97 3.15,-7.36 C2.09,-7.74 1.39,-8 0,-8 C-1.39,-8 -2.18,-7.78 -3.12,-7.37 C-4.07,-6.96 -4.67,-6.63 -5.66,-5.66 C-6.64,-4.68 -6.98,-4.1 -7.37,-3.13 C-7.78,-2.08 -8,-1.39 -8,0 C-8,1.4 -7.86,1.98 -7.47,2.87 C-7.08,3.76 -6.68,4.66 -5.66,5.66 C-4.63,6.65 -4,6.96 -3.12,7.37 C-2.25,7.78 -1.32,8 0,8 C1.32,8 1.86,7.88 2.9,7.46 C3.95,7.03 4.85,6.63 5.66,5.66 C6.46,4.69 6.78,4.45 7.29,3.29 C7.81,2.14 8,1.35 8,0c "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_1_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="67" android:startOffset="0" android:valueFrom="M8 0 C8,-1.35 7.97,-1.94 7.41,-3.02 C6.85,-4.09 6.61,-4.79 5.66,-5.66 C4.7,-6.52 4.2,-6.97 3.15,-7.36 C2.09,-7.74 1.39,-8 0,-8 C-1.39,-8 -2.18,-7.78 -3.12,-7.37 C-4.07,-6.96 -4.67,-6.63 -5.66,-5.66 C-6.64,-4.68 -6.98,-4.1 -7.37,-3.13 C-7.78,-2.08 -8,-1.39 -8,0 C-8,1.4 -7.86,1.98 -7.47,2.87 C-7.08,3.76 -6.68,4.66 -5.66,5.66 C-4.63,6.65 -4,6.96 -3.12,7.37 C-2.25,7.78 -1.32,8 0,8 C1.32,8 1.86,7.88 2.9,7.46 C3.95,7.03 4.85,6.63 5.66,5.66 C6.46,4.69 6.78,4.45 7.29,3.29 C7.81,2.14 8,1.35 8,0c " android:valueTo="M15 0 C15,-2.52 11.99,-2.3 10.94,-4.32 C9.89,-6.33 12.4,-8.98 10.61,-10.61 C8.82,-12.23 6.44,-10.15 4.46,-10.86 C2.48,-11.58 2.61,-15 0,-15 C-2.61,-15 -2.68,-11.61 -4.46,-10.84 C-6.23,-10.08 -8.76,-12.44 -10.61,-10.61 C-12.45,-8.78 -10.31,-6.69 -10.87,-4.64 C-11.43,-2.61 -15,-2.61 -15,0 C-15,2.62 -11.67,2.75 -10.94,4.42 C-10.21,6.08 -12.53,8.74 -10.61,10.61 C-8.68,12.47 -6.18,10.19 -4.54,10.96 C-2.89,11.72 -2.48,15 0,15 C2.48,15 2.49,11.78 4.45,10.99 C6.4,10.19 9.09,12.43 10.61,10.61 C12.12,8.79 10,6.52 10.97,4.36 C11.94,2.2 15,2.52 15,0c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="283" android:startOffset="67" android:valueFrom="M15 0 C15,-2.52 11.99,-2.3 10.94,-4.32 C9.89,-6.33 12.4,-8.98 10.61,-10.61 C8.82,-12.23 6.44,-10.15 4.46,-10.86 C2.48,-11.58 2.61,-15 0,-15 C-2.61,-15 -2.68,-11.61 -4.46,-10.84 C-6.23,-10.08 -8.76,-12.44 -10.61,-10.61 C-12.45,-8.78 -10.31,-6.69 -10.87,-4.64 C-11.43,-2.61 -15,-2.61 -15,0 C-15,2.62 -11.67,2.75 -10.94,4.42 C-10.21,6.08 -12.53,8.74 -10.61,10.61 C-8.68,12.47 -6.18,10.19 -4.54,10.96 C-2.89,11.72 -2.48,15 0,15 C2.48,15 2.49,11.78 4.45,10.99 C6.4,10.19 9.09,12.43 10.61,10.61 C12.12,8.79 10,6.52 10.97,4.36 C11.94,2.2 15,2.52 15,0c " android:valueTo="M7.73 0 C7.73,-1.3 7.71,-1.88 7.16,-2.92 C6.62,-3.95 6.39,-4.63 5.47,-5.47 C4.55,-6.31 4.06,-6.74 3.04,-7.11 C2.02,-7.48 1.35,-7.73 0,-7.73 C-1.34,-7.73 -2.1,-7.52 -3.02,-7.12 C-3.93,-6.73 -4.52,-6.41 -5.47,-5.47 C-6.42,-4.53 -6.75,-3.96 -7.12,-3.02 C-7.52,-2.01 -7.73,-1.35 -7.73,0 C-7.73,1.35 -7.6,1.91 -7.22,2.77 C-6.85,3.63 -6.46,4.51 -5.47,5.47 C-4.48,6.43 -3.87,6.73 -3.02,7.12 C-2.17,7.52 -1.28,7.73 0,7.73 C1.28,7.73 1.8,7.62 2.81,7.21 C3.81,6.8 4.69,6.41 5.47,5.47 C6.25,4.53 6.55,4.3 7.05,3.19 C7.55,2.07 7.73,1.3 7.73,0c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.7,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="367" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"/></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="500" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="0"
+ android:propertyName="scaleY"
+ android:startOffset="67"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M8 0 C8,-1.35 7.97,-1.94 7.41,-3.02 C6.85,-4.09 6.61,-4.79 5.66,-5.66 C4.7,-6.52 4.2,-6.97 3.15,-7.36 C2.09,-7.74 1.39,-8 0,-8 C-1.39,-8 -2.18,-7.78 -3.12,-7.37 C-4.07,-6.96 -4.67,-6.63 -5.66,-5.66 C-6.64,-4.68 -6.98,-4.1 -7.37,-3.13 C-7.78,-2.08 -8,-1.39 -8,0 C-8,1.4 -7.86,1.98 -7.47,2.87 C-7.08,3.76 -6.68,4.66 -5.66,5.66 C-4.63,6.65 -4,6.96 -3.12,7.37 C-2.25,7.78 -1.32,8 0,8 C1.32,8 1.86,7.88 2.9,7.46 C3.95,7.03 4.85,6.63 5.66,5.66 C6.46,4.69 6.78,4.45 7.29,3.29 C7.81,2.14 8,1.35 8,0c "
+ android:valueTo="M15 0 C15,-2.52 11.99,-2.3 10.94,-4.32 C9.89,-6.33 12.4,-8.98 10.61,-10.61 C8.82,-12.23 6.44,-10.15 4.46,-10.86 C2.48,-11.58 2.61,-15 0,-15 C-2.61,-15 -2.68,-11.61 -4.46,-10.84 C-6.23,-10.08 -8.76,-12.44 -10.61,-10.61 C-12.45,-8.78 -10.31,-6.69 -10.87,-4.64 C-11.43,-2.61 -15,-2.61 -15,0 C-15,2.62 -11.67,2.75 -10.94,4.42 C-10.21,6.08 -12.53,8.74 -10.61,10.61 C-8.68,12.47 -6.18,10.19 -4.54,10.96 C-2.89,11.72 -2.48,15 0,15 C2.48,15 2.49,11.78 4.45,10.99 C6.4,10.19 9.09,12.43 10.61,10.61 C12.12,8.79 10,6.52 10.97,4.36 C11.94,2.2 15,2.52 15,0c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="283"
+ android:propertyName="pathData"
+ android:startOffset="67"
+ android:valueFrom="M15 0 C15,-2.52 11.99,-2.3 10.94,-4.32 C9.89,-6.33 12.4,-8.98 10.61,-10.61 C8.82,-12.23 6.44,-10.15 4.46,-10.86 C2.48,-11.58 2.61,-15 0,-15 C-2.61,-15 -2.68,-11.61 -4.46,-10.84 C-6.23,-10.08 -8.76,-12.44 -10.61,-10.61 C-12.45,-8.78 -10.31,-6.69 -10.87,-4.64 C-11.43,-2.61 -15,-2.61 -15,0 C-15,2.62 -11.67,2.75 -10.94,4.42 C-10.21,6.08 -12.53,8.74 -10.61,10.61 C-8.68,12.47 -6.18,10.19 -4.54,10.96 C-2.89,11.72 -2.48,15 0,15 C2.48,15 2.49,11.78 4.45,10.99 C6.4,10.19 9.09,12.43 10.61,10.61 C12.12,8.79 10,6.52 10.97,4.36 C11.94,2.2 15,2.52 15,0c "
+ android:valueTo="M7.73 0 C7.73,-1.3 7.71,-1.88 7.16,-2.92 C6.62,-3.95 6.39,-4.63 5.47,-5.47 C4.55,-6.31 4.06,-6.74 3.04,-7.11 C2.02,-7.48 1.35,-7.73 0,-7.73 C-1.34,-7.73 -2.1,-7.52 -3.02,-7.12 C-3.93,-6.73 -4.52,-6.41 -5.47,-5.47 C-6.42,-4.53 -6.75,-3.96 -7.12,-3.02 C-7.52,-2.01 -7.73,-1.35 -7.73,0 C-7.73,1.35 -7.6,1.91 -7.22,2.77 C-6.85,3.63 -6.46,4.51 -5.47,5.47 C-4.48,6.43 -3.87,6.73 -3.02,7.12 C-2.17,7.52 -1.28,7.73 0,7.73 C1.28,7.73 1.8,7.62 2.81,7.21 C3.81,6.8 4.69,6.41 5.47,5.47 C6.25,4.53 6.55,4.3 7.05,3.19 C7.55,2.07 7.73,1.3 7.73,0c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.7,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="500"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="30dp"
+ android:height="30dp"
+ android:viewportHeight="30"
+ android:viewportWidth="30">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:scaleY="0"
+ android:translateX="15.441"
+ android:translateY="15.691">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-0.44 -8.69 C3.97,-8.69 7.56,-5.11 7.56,-0.69 C7.56,3.72 3.97,7.31 -0.44,7.31 C-4.86,7.31 -8.44,3.72 -8.44,-0.69 C-8.44,-5.11 -4.86,-8.69 -0.44,-8.69c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="15"
+ android:translateY="15">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M8 0 C8,-1.35 7.97,-1.94 7.41,-3.02 C6.85,-4.09 6.61,-4.79 5.66,-5.66 C4.7,-6.52 4.2,-6.97 3.15,-7.36 C2.09,-7.74 1.39,-8 0,-8 C-1.39,-8 -2.18,-7.78 -3.12,-7.37 C-4.07,-6.96 -4.67,-6.63 -5.66,-5.66 C-6.64,-4.68 -6.98,-4.1 -7.37,-3.13 C-7.78,-2.08 -8,-1.39 -8,0 C-8,1.4 -7.86,1.98 -7.47,2.87 C-7.08,3.76 -6.68,4.66 -5.66,5.66 C-4.63,6.65 -4,6.96 -3.12,7.37 C-2.25,7.78 -1.32,8 0,8 C1.32,8 1.86,7.88 2.9,7.46 C3.95,7.03 4.85,6.63 5.66,5.66 C6.46,4.69 6.78,4.45 7.29,3.29 C7.81,2.14 8,1.35 8,0c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/pin_dot_shape_5_avd.xml b/packages/SystemUI/res/drawable/pin_dot_shape_5_avd.xml
index 3779c80..9458b2e 100644
--- a/packages/SystemUI/res/drawable/pin_dot_shape_5_avd.xml
+++ b/packages/SystemUI/res/drawable/pin_dot_shape_5_avd.xml
@@ -1 +1,148 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="30dp" android:width="30dp" android:viewportHeight="30" android:viewportWidth="30"><group android:name="_R_G"><group android:name="_R_G_L_1_G" android:translateX="15.397" android:translateY="15.691" android:scaleY="0"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-0.48 -12.86 C2.96,-12.87 14.59,5.93 11.98,9.12 C9.42,12.26 5.76,11.36 -0.48,11.41 C-6.72,11.45 -10.24,11.91 -12.78,9.16 C-15.4,6.32 -3.91,-12.85 -0.48,-12.86c "/></group><group android:name="_R_G_L_0_G" android:translateX="15" android:translateY="17.64" android:pivotY="-2.64" android:scaleX="0" android:scaleY="0"><path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-4.68 -13.34 C-2.59,-17.01 2.64,-17 4.72,-13.33 C4.72,-13.33 13.65,2.45 13.65,2.45 C15.72,6.11 13.11,10.66 8.94,10.65 C8.94,10.65 -8.98,10.62 -8.98,10.62 C-13.15,10.61 -15.75,6.05 -13.67,2.4 C-13.67,2.4 -4.68,-13.34 -4.68,-13.34c "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_1_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="67" android:startOffset="0" android:valueFrom="M-0.48 -12.86 C2.96,-12.87 14.59,5.93 11.98,9.12 C9.42,12.26 5.76,11.36 -0.48,11.41 C-6.72,11.45 -10.24,11.91 -12.78,9.16 C-15.4,6.32 -3.91,-12.85 -0.48,-12.86c " android:valueTo="M-0.48 -12.86 C2.96,-12.87 14.59,5.93 11.98,9.12 C9.42,12.26 5.76,11.36 -0.48,11.41 C-6.72,11.45 -10.24,11.91 -12.78,9.16 C-15.4,6.32 -3.91,-12.85 -0.48,-12.86c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.7,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="283" android:startOffset="67" android:valueFrom="M-0.48 -12.86 C2.96,-12.87 14.59,5.93 11.98,9.12 C9.42,12.26 5.76,11.36 -0.48,11.41 C-6.72,11.45 -10.24,11.91 -12.78,9.16 C-15.4,6.32 -3.91,-12.85 -0.48,-12.86c " android:valueTo="M-0.44 -8.69 C3.98,-8.69 7.56,-5.11 7.56,-0.69 C7.56,3.73 3.98,7.31 -0.44,7.31 C-4.86,7.31 -8.44,3.73 -8.44,-0.69 C-8.44,-5.11 -4.86,-8.69 -0.44,-8.69c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.7,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="67" android:startOffset="0" android:valueFrom="M-4.68 -13.34 C-2.59,-17.01 2.64,-17 4.72,-13.33 C4.72,-13.33 13.65,2.45 13.65,2.45 C15.72,6.11 13.11,10.66 8.94,10.65 C8.94,10.65 -8.98,10.62 -8.98,10.62 C-13.15,10.61 -15.75,6.05 -13.67,2.4 C-13.67,2.4 -4.68,-13.34 -4.68,-13.34c " android:valueTo="M-4.68 -13.34 C-2.59,-17.01 2.64,-17 4.72,-13.33 C4.72,-13.33 13.65,2.45 13.65,2.45 C15.72,6.11 13.11,10.66 8.94,10.65 C8.94,10.65 -8.98,10.62 -8.98,10.62 C-13.15,10.61 -15.75,6.05 -13.67,2.4 C-13.67,2.4 -4.68,-13.34 -4.68,-13.34c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.7,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="283" android:startOffset="67" android:valueFrom="M-4.68 -13.34 C-2.59,-17.01 2.64,-17 4.72,-13.33 C4.72,-13.33 13.65,2.45 13.65,2.45 C15.72,6.11 13.11,10.66 8.94,10.65 C8.94,10.65 -8.98,10.62 -8.98,10.62 C-13.15,10.61 -15.75,6.05 -13.67,2.4 C-13.67,2.4 -4.68,-13.34 -4.68,-13.34c " android:valueTo="M-2.99 -8.52 C-1.33,-9.83 1.52,-9.34 3.25,-8.28 C4.98,-7.22 6.77,-5.88 6.7,-2.4 C6.64,1.08 4.48,2.26 3.2,3.05 C1.92,3.83 -1.1,3.72 -3.03,2.76 C-4.97,1.79 -6.38,0.3 -6.37,-2.59 C-6.36,-5.49 -4.65,-7.2 -2.99,-8.52c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.7,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="367" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"/></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="500" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:height="30dp"
+ android:width="30dp"
+ android:viewportHeight="30"
+ android:viewportWidth="30">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="15.397"
+ android:translateY="15.691"
+ android:scaleY="0">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-0.48 -12.86 C2.96,-12.87 14.59,5.93 11.98,9.12 C9.42,12.26 5.76,11.36 -0.48,11.41 C-6.72,11.45 -10.24,11.91 -12.78,9.16 C-15.4,6.32 -3.91,-12.85 -0.48,-12.86c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="15"
+ android:translateY="17.64"
+ android:pivotY="-2.64">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-4.68 -13.34 C-2.59,-17.01 2.64,-17 4.72,-13.33 C4.72,-13.33 13.65,2.45 13.65,2.45 C15.72,6.11 13.11,10.66 8.94,10.65 C8.94,10.65 -8.98,10.62 -8.98,10.62 C-13.15,10.61 -15.75,6.05 -13.67,2.4 C-13.67,2.4 -4.68,-13.34 -4.68,-13.34c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="pathData"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="M-0.48 -12.86 C2.96,-12.87 14.59,5.93 11.98,9.12 C9.42,12.26 5.76,11.36 -0.48,11.41 C-6.72,11.45 -10.24,11.91 -12.78,9.16 C-15.4,6.32 -3.91,-12.85 -0.48,-12.86c "
+ android:valueTo="M-0.48 -12.86 C2.96,-12.87 14.59,5.93 11.98,9.12 C9.42,12.26 5.76,11.36 -0.48,11.41 C-6.72,11.45 -10.24,11.91 -12.78,9.16 C-15.4,6.32 -3.91,-12.85 -0.48,-12.86c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.7,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="pathData"
+ android:duration="283"
+ android:startOffset="67"
+ android:valueFrom="M-0.48 -12.86 C2.96,-12.87 14.59,5.93 11.98,9.12 C9.42,12.26 5.76,11.36 -0.48,11.41 C-6.72,11.45 -10.24,11.91 -12.78,9.16 C-15.4,6.32 -3.91,-12.85 -0.48,-12.86c "
+ android:valueTo="M-0.44 -8.69 C3.98,-8.69 7.56,-5.11 7.56,-0.69 C7.56,3.73 3.98,7.31 -0.44,7.31 C-4.86,7.31 -8.44,3.73 -8.44,-0.69 C-8.44,-5.11 -4.86,-8.69 -0.44,-8.69c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.7,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="0"
+ android:startOffset="67"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="pathData"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="M-4.68 -13.34 C-2.59,-17.01 2.64,-17 4.72,-13.33 C4.72,-13.33 13.65,2.45 13.65,2.45 C15.72,6.11 13.11,10.66 8.94,10.65 C8.94,10.65 -8.98,10.62 -8.98,10.62 C-13.15,10.61 -15.75,6.05 -13.67,2.4 C-13.67,2.4 -4.68,-13.34 -4.68,-13.34c "
+ android:valueTo="M-4.68 -13.34 C-2.59,-17.01 2.64,-17 4.72,-13.33 C4.72,-13.33 13.65,2.45 13.65,2.45 C15.72,6.11 13.11,10.66 8.94,10.65 C8.94,10.65 -8.98,10.62 -8.98,10.62 C-13.15,10.61 -15.75,6.05 -13.67,2.4 C-13.67,2.4 -4.68,-13.34 -4.68,-13.34c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.7,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="pathData"
+ android:duration="283"
+ android:startOffset="67"
+ android:valueFrom="M-4.68 -13.34 C-2.59,-17.01 2.64,-17 4.72,-13.33 C4.72,-13.33 13.65,2.45 13.65,2.45 C15.72,6.11 13.11,10.66 8.94,10.65 C8.94,10.65 -8.98,10.62 -8.98,10.62 C-13.15,10.61 -15.75,6.05 -13.67,2.4 C-13.67,2.4 -4.68,-13.34 -4.68,-13.34c "
+ android:valueTo="M-2.99 -8.52 C-1.33,-9.83 1.52,-9.34 3.25,-8.28 C4.98,-7.22 6.77,-5.88 6.7,-2.4 C6.64,1.08 4.48,2.26 3.2,3.05 C1.92,3.83 -1.1,3.72 -3.03,2.76 C-4.97,1.79 -6.38,0.3 -6.37,-2.59 C-6.36,-5.49 -4.65,-7.2 -2.99,-8.52c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.7,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="scaleX"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="translateX"
+ android:duration="500"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/pin_dot_shape_6_avd.xml b/packages/SystemUI/res/drawable/pin_dot_shape_6_avd.xml
index ddda94a..06e27df 100644
--- a/packages/SystemUI/res/drawable/pin_dot_shape_6_avd.xml
+++ b/packages/SystemUI/res/drawable/pin_dot_shape_6_avd.xml
@@ -1 +1,142 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="30dp" android:width="30dp" android:viewportHeight="30" android:viewportWidth="30"><group android:name="_R_G"><group android:name="_R_G_L_1_G" android:translateX="15.397" android:translateY="15.691" android:scaleY="0"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-0.46 -13.65 C3.05,-13.65 12.63,-6.57 12.9,-3.63 C12.98,-2.7 12.65,12.85 -0.46,12.85 C-13.57,12.85 -13.77,-2.84 -13.76,-3.63 C-13.72,-6.87 -3.96,-13.65 -0.46,-13.65c "/></group><group android:name="_R_G_L_0_G" android:translateX="15" android:translateY="15.859" android:pivotY="-0.859" android:scaleX="0" android:scaleY="0"><path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-2.82 -14.07 C-1.14,-15.29 1.14,-15.29 2.82,-14.07 C2.82,-14.07 7.73,-10.53 7.73,-10.53 C7.73,-10.53 12.58,-7 12.58,-7 C14.29,-5.76 15,-3.55 14.35,-1.54 C14.35,-1.54 12.51,4.14 12.51,4.14 C12.51,4.14 10.64,9.85 10.64,9.85 C9.99,11.84 8.14,13.19 6.05,13.19 C6.05,13.19 0,13.2 0,13.2 C0,13.2 -6.05,13.19 -6.05,13.19 C-8.14,13.19 -9.98,11.84 -10.64,9.85 C-10.64,9.85 -12.51,4.14 -12.51,4.14 C-12.51,4.14 -14.35,-1.54 -14.35,-1.54 C-15,-3.55 -14.29,-5.76 -12.58,-7 C-12.58,-7 -7.73,-10.53 -7.73,-10.53 C-7.73,-10.53 -2.82,-14.07 -2.82,-14.07c "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_1_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="67" android:startOffset="0" android:valueFrom="M-0.46 -13.65 C3.05,-13.65 12.63,-6.57 12.9,-3.63 C12.98,-2.7 12.65,12.85 -0.46,12.85 C-13.57,12.85 -13.77,-2.84 -13.76,-3.63 C-13.72,-6.87 -3.96,-13.65 -0.46,-13.65c " android:valueTo="M-0.46 -13.65 C3.05,-13.65 12.63,-6.57 12.9,-3.63 C12.98,-2.7 12.65,12.85 -0.46,12.85 C-13.57,12.85 -13.77,-2.84 -13.76,-3.63 C-13.72,-6.87 -3.96,-13.65 -0.46,-13.65c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.8,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="283" android:startOffset="67" android:valueFrom="M-0.46 -13.65 C3.05,-13.65 12.63,-6.57 12.9,-3.63 C12.98,-2.7 12.65,12.85 -0.46,12.85 C-13.57,12.85 -13.77,-2.84 -13.76,-3.63 C-13.72,-6.87 -3.96,-13.65 -0.46,-13.65c " android:valueTo="M-0.44 -8.69 C3.98,-8.69 7.56,-5.11 7.56,-0.69 C7.56,3.73 3.98,7.31 -0.44,7.31 C-4.86,7.31 -8.44,3.73 -8.44,-0.69 C-8.44,-5.11 -4.86,-8.69 -0.44,-8.69c " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.8,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="283" android:startOffset="67" android:valueFrom="1" android:valueTo="0.35000000000000003" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="283" android:startOffset="67" android:valueFrom="1" android:valueTo="0.35000000000000003" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="367" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"/></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="500" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:height="30dp"
+ android:width="30dp"
+ android:viewportHeight="30"
+ android:viewportWidth="30">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="15.397"
+ android:translateY="15.691"
+ android:scaleY="0">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-0.46 -13.65 C3.05,-13.65 12.63,-6.57 12.9,-3.63 C12.98,-2.7 12.65,12.85 -0.46,12.85 C-13.57,12.85 -13.77,-2.84 -13.76,-3.63 C-13.72,-6.87 -3.96,-13.65 -0.46,-13.65c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="15"
+ android:translateY="15.859"
+ android:pivotY="-0.859">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-2.82 -14.07 C-1.14,-15.29 1.14,-15.29 2.82,-14.07 C2.82,-14.07 7.73,-10.53 7.73,-10.53 C7.73,-10.53 12.58,-7 12.58,-7 C14.29,-5.76 15,-3.55 14.35,-1.54 C14.35,-1.54 12.51,4.14 12.51,4.14 C12.51,4.14 10.64,9.85 10.64,9.85 C9.99,11.84 8.14,13.19 6.05,13.19 C6.05,13.19 0,13.2 0,13.2 C0,13.2 -6.05,13.19 -6.05,13.19 C-8.14,13.19 -9.98,11.84 -10.64,9.85 C-10.64,9.85 -12.51,4.14 -12.51,4.14 C-12.51,4.14 -14.35,-1.54 -14.35,-1.54 C-15,-3.55 -14.29,-5.76 -12.58,-7 C-12.58,-7 -7.73,-10.53 -7.73,-10.53 C-7.73,-10.53 -2.82,-14.07 -2.82,-14.07c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="pathData"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="M-0.46 -13.65 C3.05,-13.65 12.63,-6.57 12.9,-3.63 C12.98,-2.7 12.65,12.85 -0.46,12.85 C-13.57,12.85 -13.77,-2.84 -13.76,-3.63 C-13.72,-6.87 -3.96,-13.65 -0.46,-13.65c "
+ android:valueTo="M-0.46 -13.65 C3.05,-13.65 12.63,-6.57 12.9,-3.63 C12.98,-2.7 12.65,12.85 -0.46,12.85 C-13.57,12.85 -13.77,-2.84 -13.76,-3.63 C-13.72,-6.87 -3.96,-13.65 -0.46,-13.65c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.8,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="pathData"
+ android:duration="283"
+ android:startOffset="67"
+ android:valueFrom="M-0.46 -13.65 C3.05,-13.65 12.63,-6.57 12.9,-3.63 C12.98,-2.7 12.65,12.85 -0.46,12.85 C-13.57,12.85 -13.77,-2.84 -13.76,-3.63 C-13.72,-6.87 -3.96,-13.65 -0.46,-13.65c "
+ android:valueTo="M-0.44 -8.69 C3.98,-8.69 7.56,-5.11 7.56,-0.69 C7.56,3.73 3.98,7.31 -0.44,7.31 C-4.86,7.31 -8.44,3.73 -8.44,-0.69 C-8.44,-5.11 -4.86,-8.69 -0.44,-8.69c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.8,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="0"
+ android:startOffset="67"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="scaleX"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="67"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="scaleX"
+ android:duration="283"
+ android:startOffset="67"
+ android:valueFrom="1"
+ android:valueTo="0.35000000000000003"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.6,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="283"
+ android:startOffset="67"
+ android:valueFrom="1"
+ android:valueTo="0.35000000000000003"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.6,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="translateX"
+ android:duration="500"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
index 64234c2..e02e592 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
@@ -18,22 +18,23 @@
import android.os.Trace
import android.os.TraceNameSupplier
+import java.util.concurrent.atomic.AtomicInteger
/**
- * Run a block within a [Trace] section.
- * Calls [Trace.beginSection] before and [Trace.endSection] after the passed block.
+ * Run a block within a [Trace] section. Calls [Trace.beginSection] before and [Trace.endSection]
+ * after the passed block.
*/
inline fun <T> traceSection(tag: String, block: () -> T): T =
- if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) {
- Trace.traceBegin(Trace.TRACE_TAG_APP, tag)
- try {
- block()
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_APP)
- }
- } else {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, tag)
+ try {
block()
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_APP)
}
+ } else {
+ block()
+ }
class TraceUtils {
companion object {
@@ -43,6 +44,7 @@
/**
* Helper function for creating a Runnable object that implements TraceNameSupplier.
+ *
* This is useful for posting Runnables to Handlers with meaningful names.
*/
inline fun namedRunnable(tag: String, crossinline block: () -> Unit): Runnable {
@@ -51,5 +53,37 @@
override fun run() = block()
}
}
+
+ /**
+ * Cookie used for async traces. Shouldn't be public, but to use it inside inline methods
+ * there is no other way around.
+ */
+ val lastCookie = AtomicInteger(0)
+
+ /**
+ * Creates an async slice in a track called "AsyncTraces".
+ *
+ * This can be used to trace coroutine code. Note that all usages of this method will appear
+ * under a single track.
+ */
+ inline fun <T> traceAsync(method: String, block: () -> T): T =
+ traceAsync(method, "AsyncTraces", block)
+
+ /**
+ * Creates an async slice in a track with [trackName] while [block] runs.
+ *
+ * This can be used to trace coroutine code. [method] will be the name of the slice,
+ * [trackName] of the track. The track is one of the rows visible in a perfetto trace inside
+ * SystemUI process.
+ */
+ inline fun <T> traceAsync(method: String, trackName: String, block: () -> T): T {
+ val cookie = lastCookie.incrementAndGet()
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, trackName, method, cookie)
+ try {
+ return block()
+ } finally {
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, trackName, cookie)
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 91937af..4e0e8d0 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -91,9 +91,9 @@
field = value
if (value != null) {
smallLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" })
- value.smallClock.logBuffer = smallLogBuffer
+ value.smallClock.messageBuffer = smallLogBuffer
largeLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" })
- value.largeClock.logBuffer = largeLogBuffer
+ value.largeClock.messageBuffer = largeLogBuffer
value.initialize(resources, dozeAmount, 0f)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index f511a70..458ca2b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -544,7 +544,6 @@
public void setOnDismissAction(ActivityStarter.OnDismissAction action, Runnable cancelAction) {
if (mCancelAction != null) {
mCancelAction.run();
- mCancelAction = null;
}
mDismissAction = action;
mCancelAction = cancelAction;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index d1fffaa..76b073e 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -32,7 +32,6 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
@@ -126,7 +125,6 @@
/**
* Set the location of the lock icon.
*/
- @VisibleForTesting
public void setCenterLocation(@NonNull Point center, float radius, int drawablePadding) {
mLockIconCenter = center;
mRadius = radius;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 239a0cc..e255f5c 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -60,6 +60,7 @@
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.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.TransitionStep;
@@ -387,15 +388,17 @@
private void updateLockIconLocation() {
final float scaleFactor = mAuthController.getScaleFactor();
final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor);
- if (mUdfpsSupported) {
- mView.setCenterLocation(mAuthController.getUdfpsLocation(),
- mAuthController.getUdfpsRadius(), scaledPadding);
- } else {
- mView.setCenterLocation(
- new Point((int) mWidthPixels / 2,
- (int) (mHeightPixels
- - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))),
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ if (mUdfpsSupported) {
+ mView.setCenterLocation(mAuthController.getUdfpsLocation(),
+ mAuthController.getUdfpsRadius(), scaledPadding);
+ } else {
+ mView.setCenterLocation(
+ new Point((int) mWidthPixels / 2,
+ (int) (mHeightPixels
+ - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))),
sLockIconRadiusPx * scaleFactor, scaledPadding);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index bfe470c..ebff0b0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -595,6 +595,13 @@
// Pilfer if valid overlap, don't allow following events to reach keyguard
shouldPilfer = true;
+
+ // Touch is a valid UDFPS touch. Inform the falsing manager so that the touch
+ // isn't counted against the falsing algorithm as an accidental touch.
+ // We do this on the DOWN event instead of CANCEL/UP because the CANCEL/UP events
+ // get sent too late to this receiver (after the actual cancel/up motions occur),
+ // and therefore wouldn't end up being used as part of the falsing algo.
+ mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
break;
case UP:
@@ -614,7 +621,6 @@
data.getTime(),
data.getGestureStart(),
mStatusBarStateController.isDozing());
- mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
break;
case UNCHANGED:
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 b293ea6..db6ca0b 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
@@ -61,6 +61,7 @@
private val pin: PinBouncerViewModel by lazy {
PinBouncerViewModel(
+ applicationContext = applicationContext,
applicationScope = applicationScope,
interactor = interactor,
isInputEnabled = isInputEnabled,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index 014ebc3..641e863 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -16,6 +16,8 @@
package com.android.systemui.bouncer.ui.viewmodel
+import android.content.Context
+import com.android.keyguard.PinShapeAdapter
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import kotlinx.coroutines.CoroutineScope
@@ -29,6 +31,7 @@
/** Holds UI state and handles user input for the PIN code bouncer UI. */
class PinBouncerViewModel(
+ applicationContext: Context,
private val applicationScope: CoroutineScope,
private val interactor: BouncerInteractor,
isInputEnabled: StateFlow<Boolean>,
@@ -37,6 +40,8 @@
isInputEnabled = isInputEnabled,
) {
+ val pinShapes = PinShapeAdapter(applicationContext)
+
private val mutablePinEntries = MutableStateFlow<List<EnteredKey>>(emptyList())
val pinEntries: StateFlow<List<EnteredKey>> = mutablePinEntries
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 76002d3..40db63d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -49,6 +49,7 @@
import com.android.systemui.shortcut.ShortcutKeyDispatcher
import com.android.systemui.statusbar.notification.InstantAppNotifier
import com.android.systemui.statusbar.phone.KeyguardLiftController
+import com.android.systemui.statusbar.phone.LockscreenWallpaper
import com.android.systemui.stylus.StylusUsiPowerStartable
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.theme.ThemeOverlayController
@@ -301,4 +302,9 @@
@IntoMap
@ClassKey(KeyguardViewConfigurator::class)
abstract fun bindKeyguardViewConfigurator(impl: KeyguardViewConfigurator): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(LockscreenWallpaper::class)
+ abstract fun bindLockscreenWallpaper(impl: LockscreenWallpaper): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index f229ffe..06769dc 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -53,6 +53,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FlagsModule;
import com.android.systemui.keyboard.KeyboardModule;
+import com.android.systemui.keyguard.ui.view.layout.LockscreenLayoutModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
import com.android.systemui.log.table.TableLogBuffer;
@@ -177,6 +178,7 @@
GarbageMonitorModule.class,
KeyboardModule.class,
LetterboxModule.class,
+ LockscreenLayoutModule.class,
LogModule.class,
MediaProjectionModule.class,
MotionToolModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index d2653d9..68211ee 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -91,6 +91,15 @@
val NOTIFICATION_SHELF_REFACTOR =
unreleasedFlag(271161129, "notification_shelf_refactor", teamfood = true)
+ // TODO(b/288326013): Tracking Bug
+ @JvmField
+ val NOTIFICATION_ASYNC_HYBRID_VIEW_INFLATION =
+ unreleasedFlag(
+ 288326013,
+ "notification_async_hybrid_view_inflation",
+ teamfood = false
+ )
+
@JvmField
val ANIMATED_NOTIFICATION_SHADE_INSETS =
releasedFlag(270682168, "animated_notification_shade_insets")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index e8881a4..f59ad90 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -26,6 +26,8 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManager
+import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManagerCommandListener
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.KeyguardIndicationController
@@ -42,6 +44,8 @@
private val notificationShadeWindowView: NotificationShadeWindowView,
private val featureFlags: FeatureFlags,
private val indicationController: KeyguardIndicationController,
+ private val keyguardLayoutManager: KeyguardLayoutManager,
+ private val keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener,
) : CoreStartable {
private var indicationAreaHandle: DisposableHandle? = null
@@ -51,6 +55,8 @@
notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
bindIndicationArea(notificationPanel)
bindLockIconView(notificationPanel)
+ keyguardLayoutManager.layoutViews()
+ keyguardLayoutManagerCommandListener.start()
}
fun bindIndicationArea(legacyParent: ViewGroup) {
@@ -59,7 +65,7 @@
// At startup, 2 views with the ID `R.id.keyguard_indication_area` will be available.
// Disable one of them
if (featureFlags.isEnabled(Flags.MIGRATE_INDICATION_AREA)) {
- legacyParent.requireViewById<View>(R.id.keyguard_indication_area).let {
+ legacyParent.findViewById<View>(R.id.keyguard_indication_area)?.let {
legacyParent.removeView(it)
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 88fe155..9a32e94 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -585,17 +585,9 @@
@Override
public void onUserSwitching(int userId) {
if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
- // Note that the mLockPatternUtils user has already been updated from setCurrentUser.
- // We need to force a reset of the views, since lockNow (called by
- // ActivityManagerService) will not reconstruct the keyguard if it is already showing.
synchronized (KeyguardViewMediator.this) {
resetKeyguardDonePendingLocked();
- if (mLockPatternUtils.isLockScreenDisabled(userId)) {
- // If we are switching to a user that has keyguard disabled, dismiss keyguard.
- dismiss(null /* callback */, null /* message */);
- } else {
- resetStateLocked();
- }
+ dismiss(null /* callback */, null /* message */);
adjustStatusBarLocked();
}
}
@@ -603,16 +595,9 @@
@Override
public void onUserSwitchComplete(int userId) {
if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId));
- if (userId != UserHandle.USER_SYSTEM) {
- UserInfo info = UserManager.get(mContext).getUserInfo(userId);
- // Don't try to dismiss if the user has Pin/Pattern/Password set
- if (info == null || mLockPatternUtils.isSecure(userId)) {
- return;
- } else if (info.isGuest() || info.isDemo()) {
- // If we just switched to a guest, try to dismiss keyguard.
- dismiss(null /* callback */, null /* message */);
- }
- }
+ // We are calling dismiss again and with a delay as there are race conditions
+ // in some scenarios caused by async layout listeners
+ mHandler.postDelayed(() -> dismiss(null /* callback */, null /* message */), 500);
}
@Override
@@ -2410,58 +2395,72 @@
private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) {
@Override
public void handleMessage(Message msg) {
+ String message = "";
switch (msg.what) {
case SHOW:
+ message = "SHOW";
handleShow((Bundle) msg.obj);
break;
case HIDE:
+ message = "HIDE";
handleHide();
break;
case RESET:
+ message = "RESET";
handleReset(msg.arg1 != 0);
break;
case VERIFY_UNLOCK:
+ message = "VERIFY_UNLOCK";
Trace.beginSection("KeyguardViewMediator#handleMessage VERIFY_UNLOCK");
handleVerifyUnlock();
Trace.endSection();
break;
case NOTIFY_STARTED_GOING_TO_SLEEP:
+ message = "NOTIFY_STARTED_GOING_TO_SLEEP";
handleNotifyStartedGoingToSleep();
break;
case NOTIFY_FINISHED_GOING_TO_SLEEP:
+ message = "NOTIFY_FINISHED_GOING_TO_SLEEP";
handleNotifyFinishedGoingToSleep();
break;
case NOTIFY_STARTED_WAKING_UP:
+ message = "NOTIFY_STARTED_WAKING_UP";
Trace.beginSection(
"KeyguardViewMediator#handleMessage NOTIFY_STARTED_WAKING_UP");
handleNotifyStartedWakingUp();
Trace.endSection();
break;
case KEYGUARD_DONE:
+ message = "KEYGUARD_DONE";
Trace.beginSection("KeyguardViewMediator#handleMessage KEYGUARD_DONE");
handleKeyguardDone();
Trace.endSection();
break;
case KEYGUARD_DONE_DRAWING:
+ message = "KEYGUARD_DONE_DRAWING";
Trace.beginSection("KeyguardViewMediator#handleMessage KEYGUARD_DONE_DRAWING");
handleKeyguardDoneDrawing();
Trace.endSection();
break;
case SET_OCCLUDED:
+ message = "SET_OCCLUDED";
Trace.beginSection("KeyguardViewMediator#handleMessage SET_OCCLUDED");
handleSetOccluded(msg.arg1 != 0, msg.arg2 != 0);
Trace.endSection();
break;
case KEYGUARD_TIMEOUT:
+ message = "KEYGUARD_TIMEOUT";
synchronized (KeyguardViewMediator.this) {
doKeyguardLocked((Bundle) msg.obj);
}
break;
case DISMISS:
- final DismissMessage message = (DismissMessage) msg.obj;
- handleDismiss(message.getCallback(), message.getMessage());
+ message = "DISMISS";
+ final DismissMessage dismissMsg = (DismissMessage) msg.obj;
+ handleDismiss(dismissMsg.getCallback(), dismissMsg.getMessage());
break;
case START_KEYGUARD_EXIT_ANIM:
+ message = "START_KEYGUARD_EXIT_ANIM";
Trace.beginSection(
"KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
synchronized (KeyguardViewMediator.this) {
@@ -2479,21 +2478,25 @@
Trace.endSection();
break;
case CANCEL_KEYGUARD_EXIT_ANIM:
+ message = "CANCEL_KEYGUARD_EXIT_ANIM";
Trace.beginSection(
"KeyguardViewMediator#handleMessage CANCEL_KEYGUARD_EXIT_ANIM");
handleCancelKeyguardExitAnimation();
Trace.endSection();
break;
case KEYGUARD_DONE_PENDING_TIMEOUT:
+ message = "KEYGUARD_DONE_PENDING_TIMEOUT";
Trace.beginSection("KeyguardViewMediator#handleMessage"
+ " KEYGUARD_DONE_PENDING_TIMEOUT");
Log.w(TAG, "Timeout while waiting for activity drawn!");
Trace.endSection();
break;
case SYSTEM_READY:
+ message = "SYSTEM_READY";
handleSystemReady();
break;
}
+ Log.d(TAG, "KeyguardViewMediator queue processing message: " + message);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index b796334..ed1bf3e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -167,9 +167,10 @@
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.LOCKSCREEN,
animator =
- getDefaultAnimatorForTransitionsToState(KeyguardState.LOCKSCREEN).apply {
- duration = 0
- }
+ getDefaultAnimatorForTransitionsToState(
+ KeyguardState.LOCKSCREEN
+ )
+ .apply { duration = 0 }
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index d1ac49b..f692a39 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -49,6 +49,7 @@
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.TraceUtils.Companion.traceAsync
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -442,8 +443,10 @@
}
private suspend fun isFeatureDisabledByDevicePolicy(): Boolean =
- withContext(backgroundDispatcher) {
- devicePolicyManager.areKeyguardShortcutsDisabled(userId = userTracker.userId)
+ traceAsync("isFeatureDisabledByDevicePolicy", TAG) {
+ withContext(backgroundDispatcher) {
+ devicePolicyManager.areKeyguardShortcutsDisabled(userId = userTracker.userId)
+ }
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index ae6fc9e..0dda625 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -44,9 +44,9 @@
abstract fun start()
fun startTransitionTo(
- toState: KeyguardState,
- animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState),
- resetIfCancelled: Boolean = false
+ toState: KeyguardState,
+ animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState),
+ resetIfCancelled: Boolean = false
): UUID? {
if (
fromState != transitionInteractor.startedKeyguardState.value &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
index a62f383..0077f2d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
@@ -19,10 +19,7 @@
import android.content.Context
import android.util.AttributeSet
-import android.view.Gravity
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.widget.FrameLayout
+import androidx.constraintlayout.widget.ConstraintLayout
import com.android.keyguard.LockIconView
import com.android.systemui.R
@@ -31,7 +28,7 @@
context: Context,
private val attrs: AttributeSet?,
) :
- FrameLayout(
+ ConstraintLayout(
context,
attrs,
) {
@@ -43,31 +40,11 @@
private fun addIndicationTextArea() {
val view = KeyguardIndicationArea(context, attrs)
- addView(
- view,
- FrameLayout.LayoutParams(
- MATCH_PARENT,
- WRAP_CONTENT,
- )
- .apply {
- gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
- bottomMargin = R.dimen.keyguard_indication_margin_bottom.dp()
- }
- )
+ addView(view)
}
private fun addLockIconView() {
val view = LockIconView(context, attrs).apply { id = R.id.lock_icon_view }
- addView(
- view,
- LayoutParams(
- WRAP_CONTENT,
- WRAP_CONTENT,
- )
- )
- }
-
- private fun Int.dp(): Int {
- return context.resources.getDimensionPixelSize(this)
+ addView(view)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt
new file mode 100644
index 0000000..baaeb60
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import android.content.Context
+import android.graphics.Point
+import android.graphics.Rect
+import android.util.DisplayMetrics
+import android.view.View
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.view.WindowManager
+import androidx.annotation.VisibleForTesting
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import javax.inject.Inject
+
+/**
+ * Positions elements of the lockscreen to the default position.
+ *
+ * This will be the most common use case for phones in portrait mode.
+ */
+@SysUISingleton
+class DefaultLockscreenLayout
+@Inject
+constructor(
+ private val authController: AuthController,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val windowManager: WindowManager,
+ private val context: Context,
+) : LockscreenLayout {
+ override val id: String = DEFAULT
+
+ override fun layoutIndicationArea(rootView: KeyguardRootView) {
+ val indicationArea = rootView.findViewById<View>(R.id.keyguard_indication_area) ?: return
+
+ rootView.getConstraintSet().apply {
+ constrainWidth(indicationArea.id, MATCH_PARENT)
+ constrainHeight(indicationArea.id, WRAP_CONTENT)
+ connect(
+ indicationArea.id,
+ BOTTOM,
+ PARENT_ID,
+ BOTTOM,
+ R.dimen.keyguard_indication_margin_bottom.dp()
+ )
+ connect(indicationArea.id, START, PARENT_ID, START)
+ connect(indicationArea.id, END, PARENT_ID, END)
+ applyTo(rootView)
+ }
+ }
+
+ override fun layoutLockIcon(rootView: KeyguardRootView) {
+ val isUdfpsSupported = keyguardUpdateMonitor.isUdfpsSupported
+ val scaleFactor: Float = authController.scaleFactor
+ val mBottomPaddingPx = R.dimen.lock_icon_margin_bottom.dp()
+ val mDefaultPaddingPx = R.dimen.lock_icon_padding.dp()
+ val scaledPadding: Int = (mDefaultPaddingPx * scaleFactor).toInt()
+ val bounds = windowManager.currentWindowMetrics.bounds
+ val widthPixels = bounds.right.toFloat()
+ val heightPixels = bounds.bottom.toFloat()
+ val defaultDensity =
+ DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
+ DisplayMetrics.DENSITY_DEFAULT.toFloat()
+ val lockIconRadiusPx = (defaultDensity * 36).toInt()
+
+ if (isUdfpsSupported) {
+ authController.udfpsLocation?.let { udfpsLocation ->
+ centerLockIcon(udfpsLocation, authController.udfpsRadius, scaledPadding, rootView)
+ }
+ } else {
+ centerLockIcon(
+ Point(
+ (widthPixels / 2).toInt(),
+ (heightPixels - ((mBottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt()
+ ),
+ lockIconRadiusPx * scaleFactor,
+ scaledPadding,
+ rootView
+ )
+ }
+ }
+
+ @VisibleForTesting
+ internal fun centerLockIcon(
+ center: Point,
+ radius: Float,
+ drawablePadding: Int,
+ rootView: KeyguardRootView,
+ ) {
+ val lockIconView = rootView.findViewById<View>(R.id.lock_icon_view) ?: return
+ val lockIcon = lockIconView.findViewById<View>(R.id.lock_icon) ?: return
+ lockIcon.setPadding(drawablePadding, drawablePadding, drawablePadding, drawablePadding)
+
+ val sensorRect =
+ Rect().apply {
+ set(
+ center.x - radius.toInt(),
+ center.y - radius.toInt(),
+ center.x + radius.toInt(),
+ center.y + radius.toInt(),
+ )
+ }
+
+ rootView.getConstraintSet().apply {
+ constrainWidth(lockIconView.id, sensorRect.right - sensorRect.left)
+ constrainHeight(lockIconView.id, sensorRect.bottom - sensorRect.top)
+ connect(lockIconView.id, TOP, PARENT_ID, TOP, sensorRect.top)
+ connect(lockIconView.id, START, PARENT_ID, START, sensorRect.left)
+ applyTo(rootView)
+ }
+ }
+
+ private fun Int.dp(): Int {
+ return context.resources.getDimensionPixelSize(this)
+ }
+
+ private fun ConstraintLayout.getConstraintSet(): ConstraintSet {
+ val cs = ConstraintSet()
+ cs.clone(this)
+ return cs
+ }
+
+ companion object {
+ const val DEFAULT = "default"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt
new file mode 100644
index 0000000..9bc6302
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import android.content.res.Configuration
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.DefaultLockscreenLayout.Companion.DEFAULT
+import com.android.systemui.statusbar.policy.ConfigurationController
+import javax.inject.Inject
+
+/**
+ * Manages layout changes for the lockscreen.
+ *
+ * To add a layout, add an entry to the map with a unique id and call #transitionToLayout(string).
+ */
+@SysUISingleton
+class KeyguardLayoutManager
+@Inject
+constructor(
+ configurationController: ConfigurationController,
+ layouts: Set<@JvmSuppressWildcards LockscreenLayout>,
+ private val keyguardRootView: KeyguardRootView,
+) {
+ internal val layoutIdMap: Map<String, LockscreenLayout> = layouts.associateBy { it.id }
+ private var layout: LockscreenLayout? = layoutIdMap[DEFAULT]
+
+ init {
+ configurationController.addCallback(
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ layoutViews()
+ }
+ }
+ )
+ }
+
+ /**
+ * Transitions to a layout.
+ *
+ * @param layoutId
+ * @return whether the transition has succeeded.
+ */
+ fun transitionToLayout(layoutId: String): Boolean {
+ layout = layoutIdMap[layoutId] ?: return false
+ layoutViews()
+ return true
+ }
+
+ fun layoutViews() {
+ layout?.layoutViews(keyguardRootView)
+ }
+
+ companion object {
+ const val TAG = "KeyguardLayoutManager"
+ }
+}
+
+interface LockscreenLayout {
+ val id: String
+
+ fun layoutViews(rootView: KeyguardRootView) {
+ // Clear constraints.
+ ConstraintSet()
+ .apply {
+ clone(rootView)
+ knownIds.forEach { getConstraint(it).layout.copyFrom(ConstraintSet.Layout()) }
+ }
+ .applyTo(rootView)
+ layoutIndicationArea(rootView)
+ layoutLockIcon(rootView)
+ }
+ fun layoutIndicationArea(rootView: KeyguardRootView)
+ fun layoutLockIcon(rootView: KeyguardRootView)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt
new file mode 100644
index 0000000..b351ea8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/** Uses $ adb shell cmd statusbar layout <LayoutId> */
+class KeyguardLayoutManagerCommandListener
+@Inject
+constructor(
+ private val commandRegistry: CommandRegistry,
+ private val keyguardLayoutManager: KeyguardLayoutManager
+) {
+ private val layoutCommand = KeyguardLayoutManagerCommand()
+
+ fun start() {
+ commandRegistry.registerCommand(COMMAND) { layoutCommand }
+ }
+
+ internal inner class KeyguardLayoutManagerCommand : Command {
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ val arg = args.getOrNull(0)
+ if (arg == null || arg.lowercase() == "help") {
+ help(pw)
+ return
+ }
+
+ if (keyguardLayoutManager.transitionToLayout(arg)) {
+ pw.println("Transition succeeded!")
+ } else {
+ pw.println("Invalid argument! To see available layout ids, run:")
+ pw.println("$ adb shell cmd statusbar layout help")
+ }
+ }
+
+ override fun help(pw: PrintWriter) {
+ pw.println("Usage: $ adb shell cmd statusbar layout <layoutId>")
+ pw.println("Existing Layout Ids: ")
+ keyguardLayoutManager.layoutIdMap.forEach { entry -> pw.println("${entry.key}") }
+ }
+ }
+
+ companion object {
+ internal const val COMMAND = "layout"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt
new file mode 100644
index 0000000..00f93e3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+abstract class LockscreenLayoutModule {
+ @Binds
+ @IntoSet
+ abstract fun bindDefaultLayout(
+ defaultLockscreenLayout: DefaultLockscreenLayout
+ ): LockscreenLayout
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
index 30ee147..2883210 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
@@ -128,6 +128,15 @@
var visibilityChangedListener: ((Boolean) -> Unit)? = null
+ /**
+ * Whether the doze wake up animation is delayed and we are currently waiting for it to start.
+ */
+ var isDozeWakeUpAnimationWaiting: Boolean = false
+ set(value) {
+ field = value
+ refreshMediaPosition()
+ }
+
/** single pane media container placed at the top of the notifications list */
var singlePaneContainer: MediaContainerView? = null
private set
@@ -221,7 +230,13 @@
// by the clock. This is not the case for single-line clock though.
// For single shade, we don't need to do it, because media is a child of NSSL, which already
// gets hidden on AOD.
- return !statusBarStateController.isDozing
+ // Media also has to be hidden when waking up from dozing, and the doze wake up animation is
+ // delayed and waiting to be started.
+ // This is to stay in sync with the delaying of the horizontal alignment of the rest of the
+ // keyguard container, that is also delayed until the "wait" is over.
+ // If we show media during this waiting period, the shade will still be centered, and using
+ // the entire width of the screen, and making media show fully stretched.
+ return !statusBarStateController.isDozing && !isDozeWakeUpAnimationWaiting
}
private fun showMediaPlayer() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
index 63276fe..99daf36 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
@@ -28,18 +28,21 @@
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.util.Log;
+import androidx.annotation.NonNull;
+
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,22 +67,21 @@
private Context mCurrentUserContext;
private final IOverlayManager mOverlayManager;
private final Executor mUiBgExecutor;
+ private final UserTracker mUserTracker;
private ArrayList<ModeChangedListener> mListeners = new ArrayList<>();
- private final DeviceProvisionedController.DeviceProvisionedListener mDeviceProvisionedCallback =
- new DeviceProvisionedController.DeviceProvisionedListener() {
- @Override
- public void onUserSwitched() {
- if (DEBUG) {
- Log.d(TAG, "onUserSwitched: "
- + ActivityManagerWrapper.getInstance().getCurrentUserId());
- }
+ private final UserTracker.Callback mUserTrackerCallback = new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ if (DEBUG) {
+ Log.d(TAG, "onUserChanged: "
+ + newUser);
+ }
- // Update the nav mode for the current user
- updateCurrentInteractionMode(true /* notify */);
- }
- };
+ updateCurrentInteractionMode(true /* notify */);
+ }
+ };
// The primary user SysUI process doesn't get AppInfo changes from overlay package changes for
// the secondary user (b/158613864), so we need to update the interaction mode here as well
@@ -97,19 +99,20 @@
@Inject
public NavigationModeController(Context context,
- DeviceProvisionedController deviceProvisionedController,
ConfigurationController configurationController,
+ UserTracker userTracker,
+ @Main Executor mainExecutor,
@UiBackground Executor uiBgExecutor,
DumpManager dumpManager) {
mContext = context;
mCurrentUserContext = context;
+ mUserTracker = userTracker;
+ mUserTracker.addCallback(mUserTrackerCallback, mainExecutor);
mOverlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
mUiBgExecutor = uiBgExecutor;
dumpManager.registerDumpable(getClass().getSimpleName(), this);
- deviceProvisionedController.addCallback(mDeviceProvisionedCallback);
-
IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
overlayFilter.addDataScheme("package");
overlayFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL);
@@ -129,6 +132,7 @@
}
public void updateCurrentInteractionMode(boolean notify) {
+ Trace.beginSection("NMC#updateCurrentInteractionMode");
mCurrentUserContext = getCurrentUserContext();
int mode = getCurrentInteractionMode(mCurrentUserContext);
mUiBgExecutor.execute(() ->
@@ -144,6 +148,7 @@
mListeners.get(i).onNavigationModeChanged(mode);
}
}
+ Trace.endSection();
}
public int addListener(ModeChangedListener listener) {
@@ -171,7 +176,7 @@
}
public Context getCurrentUserContext() {
- int userId = ActivityManagerWrapper.getInstance().getCurrentUserId();
+ int userId = mUserTracker.getUserId();
if (DEBUG) {
Log.d(TAG, "getCurrentUserContext: contextUser=" + mContext.getUserId()
+ " currentUser=" + userId);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 004d563..d97db3b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -117,6 +117,7 @@
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog;
@@ -208,7 +209,6 @@
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
@@ -238,7 +238,7 @@
import kotlinx.coroutines.CoroutineDispatcher;
-@CentralSurfacesComponent.CentralSurfacesScope
+@SysUISingleton
public final class NotificationPanelViewController implements ShadeSurface, Dumpable {
public static final String TAG = NotificationPanelView.class.getSimpleName();
@@ -1409,11 +1409,13 @@
mKeyguardBottomArea = keyguardBottomArea;
}
- void setOpenCloseListener(OpenCloseListener openCloseListener) {
+ @Override
+ public void setOpenCloseListener(OpenCloseListener openCloseListener) {
mOpenCloseListener = openCloseListener;
}
- void setTrackingStartedListener(TrackingStartedListener trackingStartedListener) {
+ @Override
+ public void setTrackingStartedListener(TrackingStartedListener trackingStartedListener) {
mTrackingStartedListener = trackingStartedListener;
}
@@ -1625,6 +1627,7 @@
mWillPlayDelayedDozeAmountAnimation = willPlay;
mWakeUpCoordinator.logDelayingClockWakeUpAnimation(willPlay);
+ mKeyguardMediaController.setDozeWakeUpAnimationWaiting(willPlay);
// Once changing this value, see if we should move the clock.
positionClockAndNotifications();
@@ -3380,11 +3383,13 @@
ViewGroupFadeHelper.reset(mView);
}
- void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
+ @Override
+ public void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
mView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
}
- void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
+ @Override
+ public void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
@@ -3854,8 +3859,8 @@
return !isFullyCollapsed() && !mTracking && !mClosing;
}
- /** Collapses the shade instantly without animation. */
- void instantCollapse() {
+ @Override
+ public void instantCollapse() {
abortAnimations();
setExpandedFraction(0f);
if (mExpanding) {
@@ -4028,8 +4033,8 @@
mFixedDuration = NO_FIXED_DURATION;
}
- /** */
- boolean postToView(Runnable action) {
+ @Override
+ public boolean postToView(Runnable action) {
return mView.post(action);
}
@@ -5124,18 +5129,5 @@
return super.performAccessibilityAction(host, action, args);
}
}
-
- /** Listens for when touch tracking begins. */
- interface TrackingStartedListener {
- void onTrackingStarted();
- }
-
- /** Listens for when shade begins opening of finishes closing. */
- interface OpenCloseListener {
- /** Called when the shade finishes closing. */
- void onClosingFinished();
- /** Called when the shade starts opening. */
- void onOpenStarted();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index e95c3435..025c4611 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -68,6 +68,7 @@
import com.android.systemui.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
@@ -98,7 +99,6 @@
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.LargeScreenUtils;
@@ -113,7 +113,7 @@
/** Handles QuickSettings touch handling, expansion and animation state
* TODO (b/264460656) make this dumpable
*/
-@CentralSurfacesComponent.CentralSurfacesScope
+@SysUISingleton
public class QuickSettingsController implements Dumpable {
public static final String TAG = "QuickSettingsController";
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
index 9ed0e9a..317d885 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
@@ -165,8 +165,7 @@
NotificationShadeWindowViewController notificationShadeWindowViewController);
/** */
- void setNotificationPanelViewController(
- NotificationPanelViewController notificationPanelViewController);
+ void setShadeViewController(ShadeViewController shadeViewController);
/** Listens for shade visibility changes. */
interface ShadeVisibilityListener {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index c9338b3..b92afac 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -70,7 +70,8 @@
private boolean mExpandedVisible;
- private NotificationPanelViewController mNotificationPanelViewController;
+ // TODO(b/237661616): Rename this variable to mShadeViewController.
+ private ShadeViewController mNotificationPanelViewController;
private NotificationPresenter mPresenter;
private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
private ShadeVisibilityListener mShadeVisibilityListener;
@@ -426,12 +427,11 @@
}
@Override
- public void setNotificationPanelViewController(
- NotificationPanelViewController notificationPanelViewController) {
- mNotificationPanelViewController = notificationPanelViewController;
+ public void setShadeViewController(ShadeViewController shadeViewController) {
+ mNotificationPanelViewController = shadeViewController;
mNotificationPanelViewController.setTrackingStartedListener(this::runPostCollapseRunnables);
mNotificationPanelViewController.setOpenCloseListener(
- new NotificationPanelViewController.OpenCloseListener() {
+ new OpenCloseListener() {
@Override
public void onClosingFinished() {
ShadeControllerImpl.this.onClosingFinished();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index d44f9ec..2c560c9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -67,6 +67,12 @@
@ClassKey(AuthRippleController::class)
abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeViewController(
+ notificationPanelViewController: NotificationPanelViewController
+ ): ShadeViewController
+
companion object {
const val SHADE_HEADER = "large_screen_shade_header"
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index 3d9fcf9..9aa5eb0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -17,6 +17,7 @@
import android.view.MotionEvent
import android.view.ViewGroup
+import android.view.ViewTreeObserver
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.statusbar.RemoteInputController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -77,6 +78,9 @@
/** Collapses the shade with an animation duration in milliseconds. */
fun collapseWithDuration(animationDuration: Int)
+ /** Collapses the shade instantly without animation. */
+ fun instantCollapse()
+
/**
* Animate QS collapse by flinging it. If QS is expanded, it will collapse into QQS and stop. If
* in split shade, it will collapse the whole shade.
@@ -100,6 +104,9 @@
/** Returns whether the shade's top level view is enabled. */
val isViewEnabled: Boolean
+ /** Sets a listener to be notified when the shade starts opening or finishes closing. */
+ fun setOpenCloseListener(openCloseListener: OpenCloseListener)
+
/** Returns whether status bar icons should be hidden when the shade is expanded. */
fun shouldHideStatusBarIconsWhenExpanded(): Boolean
@@ -109,6 +116,9 @@
*/
fun blockExpansionForCurrentTouch()
+ /** Sets a listener to be notified when touch tracking begins. */
+ fun setTrackingStartedListener(trackingStartedListener: TrackingStartedListener)
+
/**
* Disables the shade header.
*
@@ -178,6 +188,15 @@
/** Ensures that the touchable region is updated. */
fun updateTouchableRegion()
+ /** Adds a global layout listener. */
+ fun addOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener)
+
+ /** Removes a global layout listener. */
+ fun removeOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener)
+
+ /** Posts the given runnable to the view. */
+ fun postToView(action: Runnable): Boolean
+
// ******* Begin Keyguard Section *********
/** Animate to expanded shade after a delay in ms. Used for lockscreen to shade transition. */
fun transitionToExpandedShade(delay: Long)
@@ -337,3 +356,17 @@
/** Return the fraction of the shade that's expanded, when in lockscreen. */
val lockscreenShadeDragProgress: Float
}
+
+/** Listens for when touch tracking begins. */
+interface TrackingStartedListener {
+ fun onTrackingStarted()
+}
+
+/** Listens for when shade begins opening or finishes closing. */
+interface OpenCloseListener {
+ /** Called when the shade finishes closing. */
+ fun onClosingFinished()
+
+ /** Called when the shade starts opening. */
+ fun onOpenStarted()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 075b41b..035fa04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -41,6 +41,8 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeSurface;
import com.android.systemui.shade.carrier.ShadeCarrierGroupController;
import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
@@ -273,6 +275,21 @@
return ongoingCallController;
}
+ /**
+ * {@link NotificationPanelViewController} implements two interfaces:
+ * - {@link com.android.systemui.shade.ShadeViewController}, which can be used by any class
+ * needing access to the shade.
+ * - {@link ShadeSurface}, which should *only* be used by {@link CentralSurfacesImpl}.
+ *
+ * Since {@link ShadeSurface} should only be accessible by {@link CentralSurfacesImpl}, it's
+ * *only* bound in this CentralSurfaces dependencies module.
+ * The {@link com.android.systemui.shade.ShadeViewController} interface is bound in
+ * {@link com.android.systemui.shade.ShadeModule} so others can access it.
+ */
+ @Binds
+ @SysUISingleton
+ ShadeSurface provideShadeSurface(NotificationPanelViewController impl);
+
/** */
@Binds
ShadeCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 8af488e..27510d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -359,9 +359,9 @@
}
@Override
- public long performRemoveAnimation(long duration, long delay,
- float translationDirection, boolean isHeadsUpAnimation, float endLocation,
- Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
+ public long performRemoveAnimation(long duration, long delay, float translationDirection,
+ boolean isHeadsUpAnimation, Runnable onFinishedRunnable,
+ AnimatorListenerAdapter animationListener) {
enableAppearDrawing(true);
mIsHeadsUpAnimation = isHeadsUpAnimation;
if (mDrawingAppearAnimation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 30747db..b34c281 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2975,7 +2975,6 @@
long delay,
float translationDirection,
boolean isHeadsUpAnimation,
- float endLocation,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
if (mMenuRow != null && mMenuRow.isMenuVisible()) {
@@ -2986,7 +2985,7 @@
public void onAnimationEnd(Animator animation) {
ExpandableNotificationRow.super.performRemoveAnimation(
duration, delay, translationDirection, isHeadsUpAnimation,
- endLocation, onFinishedRunnable, animationListener);
+ onFinishedRunnable, animationListener);
}
});
anim.start();
@@ -2994,7 +2993,7 @@
}
}
return super.performRemoveAnimation(duration, delay, translationDirection,
- isHeadsUpAnimation, endLocation, onFinishedRunnable, animationListener);
+ isHeadsUpAnimation, onFinishedRunnable, animationListener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index f0e15c2..f986244 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -367,7 +367,6 @@
* such that the child appears to be going away to the top. 1
* Should mean the opposite.
* @param isHeadsUpAnimation Is this a headsUp animation.
- * @param endLocation The location where the horizonal heads up disappear animation should end.
* @param onFinishedRunnable A runnable which should be run when the animation is finished.
* @param animationListener An animation listener to add to the animation.
*
@@ -375,7 +374,7 @@
* animation starts.
*/
public abstract long performRemoveAnimation(long duration,
- long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+ long delay, float translationDirection, boolean isHeadsUpAnimation,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index b24cec1..0c686be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -235,7 +235,7 @@
@Override
public long performRemoveAnimation(long duration, long delay,
- float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+ float translationDirection, boolean isHeadsUpAnimation,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
// TODO: Use duration
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
index b8f28b5..04308b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
@@ -69,11 +69,14 @@
canvas.clipPath(clipPath)
}
-
- override fun performRemoveAnimation(duration: Long, delay: Long, translationDirection: Float,
- isHeadsUpAnimation: Boolean, endLocation: Float,
- onFinishedRunnable: Runnable?,
- animationListener: AnimatorListenerAdapter?): Long {
+ override fun performRemoveAnimation(
+ duration: Long,
+ delay: Long,
+ translationDirection: Float,
+ isHeadsUpAnimation: Boolean,
+ onFinishedRunnable: Runnable?,
+ animationListener: AnimatorListenerAdapter?
+ ): Long {
return 0
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index d73919b..2742a23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -27,8 +27,6 @@
import com.android.systemui.R;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
@@ -427,7 +425,7 @@
}
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
- 0, postAnimation, null);
+ postAnimation, null);
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
if (mHostLayout.isFullySwipedOut(changingView)) {
@@ -474,28 +472,12 @@
mTmpState.initFrom(changingView);
endRunnable = changingView::removeFromTransientContainer;
}
- float targetLocation = 0;
boolean needsAnimation = true;
if (changingView instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) changingView;
if (row.isDismissed()) {
needsAnimation = false;
}
-
- NotificationEntry entry = row.getEntry();
- StatusBarIconView icon = entry.getIcons().getStatusBarIcon();
- final StatusBarIconView centeredIcon = entry.getIcons().getCenteredIcon();
- if (centeredIcon != null && centeredIcon.getParent() != null) {
- icon = centeredIcon;
- }
- if (icon.getParent() != null) {
- icon.getLocationOnScreen(mTmpLocation);
- float iconPosition = mTmpLocation[0] - icon.getTranslationX()
- + ViewState.getFinalTranslationX(icon)
- + icon.getWidth() * 0.25f;
- mHostLayout.getLocationOnScreen(mTmpLocation);
- targetLocation = iconPosition - mTmpLocation[0];
- }
}
if (needsAnimation) {
@@ -515,7 +497,7 @@
}
long removeAnimationDelay = changingView.performRemoveAnimation(
ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
- 0, 0.0f, true /* isHeadsUpAppear */, targetLocation,
+ 0, 0.0f, true /* isHeadsUpAppear */,
postAnimation, getGlobalAnimationFinishedListener());
mAnimationProperties.delay += removeAnimationDelay;
} else if (endRunnable != null) {
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 8b5db28..0d3dfae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -182,7 +182,6 @@
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.shade.CameraLauncher;
-import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.QuickSettingsController;
@@ -477,14 +476,12 @@
private final Lazy<LightRevealScrimViewModel> mLightRevealScrimViewModelLazy;
/** Controller for the Shade. */
- @VisibleForTesting
- ShadeSurface mShadeSurface;
+ private final ShadeSurface mShadeSurface;
private final ShadeLogger mShadeLogger;
// settings
private QSPanelController mQSPanelController;
- @VisibleForTesting
- QuickSettingsController mQsController;
+ private final QuickSettingsController mQsController;
KeyguardIndicationController mKeyguardIndicationController;
@@ -704,9 +701,11 @@
MetricsLogger metricsLogger,
ShadeLogger shadeLogger,
@UiBackground Executor uiBgExecutor,
+ ShadeSurface shadeSurface,
NotificationMediaManager notificationMediaManager,
NotificationLockscreenUserManager lockScreenUserManager,
NotificationRemoteInputManager remoteInputManager,
+ QuickSettingsController quickSettingsController,
UserSwitcherController userSwitcherController,
BatteryController batteryController,
SysuiColorExtractor colorExtractor,
@@ -805,9 +804,11 @@
mMetricsLogger = metricsLogger;
mShadeLogger = shadeLogger;
mUiBgExecutor = uiBgExecutor;
+ mShadeSurface = shadeSurface;
mMediaManager = notificationMediaManager;
mLockscreenUserManager = lockScreenUserManager;
mRemoteInputManager = remoteInputManager;
+ mQsController = quickSettingsController;
mUserSwitcherController = userSwitcherController;
mBatteryController = batteryController;
mColorExtractor = colorExtractor;
@@ -1611,13 +1612,9 @@
// (Right now, there's a circular dependency.)
mNotificationShadeWindowController.setWindowRootView(windowRootView);
mNotificationShadeWindowViewController.setupExpandedStatusBar();
- NotificationPanelViewController npvc =
- mCentralSurfacesComponent.getNotificationPanelViewController();
- mShadeSurface = npvc;
- mShadeController.setNotificationPanelViewController(npvc);
+ mShadeController.setShadeViewController(mShadeSurface);
mShadeController.setNotificationShadeWindowViewController(
mNotificationShadeWindowViewController);
- mQsController = mCentralSurfacesComponent.getQuickSettingsController();
mBackActionInteractor.setup(mQsController, mShadeSurface);
mPresenter = mCentralSurfacesComponent.getNotificationPresenter();
mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter();
@@ -1813,7 +1810,7 @@
@Override
public void onStatusBarTrackpadEvent(MotionEvent event) {
- mCentralSurfacesComponent.getNotificationPanelViewController().handleExternalTouch(event);
+ mShadeSurface.handleExternalTouch(event);
}
private void onExpandedInvisible() {
@@ -2180,9 +2177,6 @@
*/
@Override
public void setLockscreenUser(int newUserId) {
- if (mLockscreenWallpaper != null && !mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
- mLockscreenWallpaper.setCurrentUser(newUserId);
- }
if (mWallpaperSupported) {
mWallpaperChangedReceiver.onReceive(mContext, null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index c07b5e0..b2c39f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -40,12 +40,17 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.user.data.model.SelectedUserModel;
+import com.android.systemui.user.data.model.SelectionStatus;
+import com.android.systemui.user.data.repository.UserRepository;
+import com.android.systemui.util.kotlin.JavaAdapter;
import libcore.io.IoUtils;
@@ -59,7 +64,7 @@
*/
@SysUISingleton
public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implements Runnable,
- Dumpable {
+ Dumpable, CoreStartable {
private static final String TAG = "LockscreenWallpaper";
@@ -72,6 +77,8 @@
private final WallpaperManager mWallpaperManager;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final Handler mH;
+ private final JavaAdapter mJavaAdapter;
+ private final UserRepository mUserRepository;
private boolean mCached;
private Bitmap mCache;
@@ -88,6 +95,8 @@
DumpManager dumpManager,
NotificationMediaManager mediaManager,
@Main Handler mainHandler,
+ JavaAdapter javaAdapter,
+ UserRepository userRepository,
UserTracker userTracker) {
dumpManager.registerDumpable(getClass().getSimpleName(), this);
mWallpaperManager = wallpaperManager;
@@ -95,6 +104,8 @@
mUpdateMonitor = keyguardUpdateMonitor;
mMediaManager = mediaManager;
mH = mainHandler;
+ mJavaAdapter = javaAdapter;
+ mUserRepository = userRepository;
if (iWallpaperManager != null && !mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
// Service is disabled on some devices like Automotive
@@ -106,6 +117,14 @@
}
}
+ @Override
+ public void start() {
+ if (!isLockscreenLiveWallpaperEnabled()) {
+ mJavaAdapter.alwaysCollectFlow(
+ mUserRepository.getSelectedUser(), this::setSelectedUser);
+ }
+ }
+
public Bitmap getBitmap() {
assertLockscreenLiveWallpaperNotEnabled();
@@ -169,9 +188,15 @@
}
}
- public void setCurrentUser(int user) {
+ private void setSelectedUser(SelectedUserModel selectedUserModel) {
assertLockscreenLiveWallpaperNotEnabled();
+ if (selectedUserModel.getSelectionStatus().equals(SelectionStatus.SELECTION_IN_PROGRESS)) {
+ // Wait until the selection has finished before updating.
+ return;
+ }
+
+ int user = selectedUserModel.getUserInfo().id;
if (user != mCurrentUserId) {
if (mSelectedUser == null || user != mSelectedUser.getIdentifier()) {
mCached = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
index c618be8..4ae460a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
@@ -21,10 +21,8 @@
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.systemui.scene.ui.view.WindowRootView;
-import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
-import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeHeaderController;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -89,14 +87,6 @@
NotificationShadeWindowViewController getNotificationShadeWindowViewController();
/**
- * Creates a NotificationPanelViewController.
- */
- NotificationPanelViewController getNotificationPanelViewController();
-
- /** Creates a QuickSettingsController. */
- QuickSettingsController getQuickSettingsController();
-
- /**
* Creates a StatusBarHeadsUpChangeListener.
*/
StatusBarHeadsUpChangeListener getStatusBarHeadsUpChangeListener();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 421bdc6..ebdde78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -21,18 +21,15 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
-import com.android.systemui.statusbar.phone.SystemBarAttributesListener;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
@@ -44,10 +41,8 @@
import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.settings.SecureSettings;
-import dagger.Binds;
import dagger.Module;
import dagger.Provides;
-import dagger.multibindings.IntoSet;
import java.util.concurrent.Executor;
@@ -65,17 +60,6 @@
public static final String STATUS_BAR_FRAGMENT = "status_bar_fragment";
- /** */
- @Binds
- @CentralSurfacesComponent.CentralSurfacesScope
- abstract ShadeViewController bindsShadeViewController(
- NotificationPanelViewController notificationPanelViewController);
-
- @Binds
- @IntoSet
- abstract StatusBarBoundsProvider.BoundsChangeListener sysBarAttrsListenerAsBoundsListener(
- SystemBarAttributesListener systemBarAttributesListener);
-
/**
* Creates a new {@link CollapsedStatusBarFragment}.
*
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 8f9f019..eb4d963 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
@@ -27,20 +27,23 @@
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.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;
-import dagger.Module;
-import dagger.Provides;
-import dagger.multibindings.Multibinds;
-
/** Dagger module for {@link StatusBarFragmentComponent}. */
@Module
public interface StatusBarFragmentModule {
@@ -152,4 +155,10 @@
/** */
@Multibinds
Set<StatusBarBoundsProvider.BoundsChangeListener> boundsChangeListeners();
+
+ /** */
+ @Binds
+ @IntoSet
+ StatusBarBoundsProvider.BoundsChangeListener sysBarAttrsListenerAsBoundsListener(
+ SystemBarAttributesListener systemBarAttributesListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
index a4fbc2c..a57be66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
@@ -96,7 +96,7 @@
networkId = DEMO_NET_ID,
isValidated = validated ?: true,
level = level ?: 0,
- ssid = ssid,
+ ssid = ssid ?: DEMO_NET_SSID,
// These fields below aren't supported in demo mode, since they aren't needed to satisfy
// the interface.
@@ -115,5 +115,6 @@
companion object {
private const val DEMO_NET_ID = 1234
+ private const val DEMO_NET_SSID = "Demo SSID"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index b135d0d..1c3a8850 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -28,6 +28,7 @@
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerExecutor;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings.Global;
@@ -122,7 +123,12 @@
userTracker.getUserId()) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
- updateZenModeConfig();
+ try {
+ Trace.beginSection("updateZenModeConfig");
+ updateZenModeConfig();
+ } finally {
+ Trace.endSection();
+ }
}
};
mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index bcf3b0c..4a9921e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -240,8 +240,10 @@
Insets.of(0, safeTouchRegionHeight, 0, 0));
}
lp.providedInsets = new InsetsFrameProvider[] {
- new InsetsFrameProvider(mInsetsSourceOwner, 0, statusBars()),
- new InsetsFrameProvider(mInsetsSourceOwner, 0, tappableElement()),
+ new InsetsFrameProvider(mInsetsSourceOwner, 0, statusBars())
+ .setInsetsSize(Insets.of(0, height, 0, 0)),
+ new InsetsFrameProvider(mInsetsSourceOwner, 0, tappableElement())
+ .setInsetsSize(Insets.of(0, height, 0, 0)),
gestureInsetsProvider
};
return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/model/SelectedUserModel.kt b/packages/SystemUI/src/com/android/systemui/user/data/model/SelectedUserModel.kt
new file mode 100644
index 0000000..cefd466
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/data/model/SelectedUserModel.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.user.data.model
+
+import android.content.pm.UserInfo
+
+/** A model for the currently selected user. */
+data class SelectedUserModel(
+ /** Information about the user. */
+ val userInfo: UserInfo,
+ /** The current status of the selection. */
+ val selectionStatus: SelectionStatus,
+)
+
+/** The current status of the selection. */
+enum class SelectionStatus {
+ /** This user has started being selected but the selection hasn't completed. */
+ SELECTION_IN_PROGRESS,
+ /** The selection of this user has completed. */
+ SELECTION_COMPLETE,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 3de75ca..954765c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -33,6 +33,8 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.settings.UserTracker
+import com.android.systemui.user.data.model.SelectedUserModel
+import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
@@ -69,6 +71,9 @@
/** List of all users on the device. */
val userInfos: Flow<List<UserInfo>>
+ /** Information about the currently-selected user, including [UserInfo] and other details. */
+ val selectedUser: StateFlow<SelectedUserModel>
+
/** [UserInfo] of the currently-selected user. */
val selectedUserInfo: Flow<UserInfo>
@@ -146,9 +151,6 @@
private val _userInfos = MutableStateFlow<List<UserInfo>?>(null)
override val userInfos: Flow<List<UserInfo>> = _userInfos.filterNotNull()
- private val _selectedUserInfo = MutableStateFlow<UserInfo?>(null)
- override val selectedUserInfo: Flow<UserInfo> = _selectedUserInfo.filterNotNull()
-
override var mainUserId: Int = UserHandle.USER_NULL
private set
override var lastSelectedNonGuestUserId: Int = UserHandle.USER_NULL
@@ -174,12 +176,57 @@
override var isRefreshUsersPaused: Boolean = false
init {
- observeSelectedUser()
if (featureFlags.isEnabled(FACE_AUTH_REFACTOR)) {
observeUserSwitching()
}
}
+ override val selectedUser: StateFlow<SelectedUserModel> = run {
+ // Some callbacks don't modify the selection status, so maintain the current value.
+ var currentSelectionStatus = SelectionStatus.SELECTION_COMPLETE
+ conflatedCallbackFlow {
+ fun send(selectionStatus: SelectionStatus) {
+ currentSelectionStatus = selectionStatus
+ trySendWithFailureLogging(
+ SelectedUserModel(tracker.userInfo, selectionStatus),
+ TAG,
+ )
+ }
+
+ val callback =
+ object : UserTracker.Callback {
+ override fun onUserChanging(newUser: Int, userContext: Context) {
+ send(SelectionStatus.SELECTION_IN_PROGRESS)
+ }
+
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ send(SelectionStatus.SELECTION_COMPLETE)
+ }
+
+ override fun onProfilesChanged(profiles: List<UserInfo>) {
+ send(currentSelectionStatus)
+ }
+ }
+
+ tracker.addCallback(callback, mainDispatcher.asExecutor())
+ send(currentSelectionStatus)
+
+ awaitClose { tracker.removeCallback(callback) }
+ }
+ .onEach {
+ if (!it.userInfo.isGuest) {
+ lastSelectedNonGuestUserId = it.userInfo.id
+ }
+ }
+ .stateIn(
+ applicationScope,
+ SharingStarted.Eagerly,
+ initialValue = SelectedUserModel(tracker.userInfo, currentSelectionStatus)
+ )
+ }
+
+ override val selectedUserInfo: Flow<UserInfo> = selectedUser.map { it.userInfo }
+
override fun refreshUsers() {
applicationScope.launch {
val result = withContext(backgroundDispatcher) { manager.aliveUsers }
@@ -201,7 +248,7 @@
}
override fun getSelectedUserInfo(): UserInfo {
- return checkNotNull(_selectedUserInfo.value)
+ return selectedUser.value.userInfo
}
override fun isSimpleUserSwitcher(): Boolean {
@@ -234,38 +281,6 @@
.launchIn(applicationScope)
}
- private fun observeSelectedUser() {
- conflatedCallbackFlow {
- fun send() {
- trySendWithFailureLogging(tracker.userInfo, TAG)
- }
-
- val callback =
- object : UserTracker.Callback {
- override fun onUserChanging(newUser: Int, userContext: Context) {
- send()
- }
-
- override fun onProfilesChanged(profiles: List<UserInfo>) {
- send()
- }
- }
-
- tracker.addCallback(callback, mainDispatcher.asExecutor())
- send()
-
- awaitClose { tracker.removeCallback(callback) }
- }
- .onEach {
- if (!it.isGuest) {
- lastSelectedNonGuestUserId = it.id
- }
-
- _selectedUserInfo.value = it
- }
- .launchIn(applicationScope)
- }
-
private suspend fun getSettings(): UserSwitcherSettingsModel {
return withContext(backgroundDispatcher) {
val isSimpleUserSwitcher =
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 0dcd404..2318988 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -21,21 +21,22 @@
import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.log.LogBuffer
import com.android.systemui.plugins.ClockAnimations
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockEvents
-import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockFaceConfig
+import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockFaceEvents
import com.android.systemui.plugins.ClockTickRate
-import com.android.systemui.log.LogBuffer
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -64,7 +65,6 @@
import org.mockito.junit.MockitoJUnit
import java.util.TimeZone
import java.util.concurrent.Executor
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@@ -122,7 +122,9 @@
bouncerRepository = bouncerRepository,
configurationRepository = FakeConfigurationRepository(),
),
- KeyguardTransitionInteractor(transitionRepository, TestScope().backgroundScope),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ ).keyguardTransitionInteractor,
broadcastDispatcher,
batteryController,
keyguardUpdateMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index c88c4d6..e7e1cc9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -19,6 +19,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR;
+import static com.android.systemui.flags.Flags.MIGRATE_LOCK_ICON;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
@@ -143,6 +144,7 @@
mFeatureFlags = new FakeFeatureFlags();
mFeatureFlags.set(FACE_AUTH_REFACTOR, false);
+ mFeatureFlags.set(MIGRATE_LOCK_ICON, false);
mUnderTest = new LockIconViewController(
mLockIconView,
mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index eb9ce1d..58982d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -22,6 +22,7 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.internal.util.FunctionalUtils.ThrowingConsumer;
+import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -59,6 +60,7 @@
import android.os.RemoteException;
import android.os.VibrationAttributes;
import android.testing.TestableLooper.RunWithLooper;
+import android.util.Pair;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
@@ -1203,8 +1205,53 @@
}
@Test
+ public void fingerDown_falsingManagerInformed() throws RemoteException {
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenAcceptFingerDownEvent();
+
+ // WHEN ACTION_DOWN is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ mBiometricExecutor.runAllReady();
+ downEvent.recycle();
+
+ // THEN falsing manager is informed of the touch
+ verify(mFalsingManager).isFalseTouch(UDFPS_AUTHENTICATION);
+ }
+
+ @Test
public void onTouch_withNewTouchDetection_shouldCallNewFingerprintManagerPath()
throws RemoteException {
+ final Pair<TouchProcessorResult, TouchProcessorResult> processorResultDownAndUp =
+ givenAcceptFingerDownEvent();
+
+ // WHEN ACTION_DOWN is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDownAndUp.first);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ mBiometricExecutor.runAllReady();
+ downEvent.recycle();
+
+ // AND ACTION_UP is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDownAndUp.second);
+ MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
+ mBiometricExecutor.runAllReady();
+ upEvent.recycle();
+
+ // THEN the new FingerprintManager path is invoked.
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ verify(mFingerprintManager).onPointerUp(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ }
+
+ private Pair<TouchProcessorResult, TouchProcessorResult> givenAcceptFingerDownEvent()
+ throws RemoteException {
final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
0L);
final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
@@ -1230,27 +1277,7 @@
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
- // WHEN ACTION_DOWN is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDown);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- // AND ACTION_UP is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultUp);
- MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
- mBiometricExecutor.runAllReady();
- upEvent.recycle();
-
- // THEN the new FingerprintManager path is invoked.
- verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
- verify(mFingerprintManager).onPointerUp(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ return new Pair<>(processorResultDown, processorResultUp);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
index 263ce1a..8f8004f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
@@ -7,7 +7,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -54,10 +54,11 @@
LogContextInteractorImpl(
testScope.backgroundScope,
foldProvider,
- KeyguardTransitionInteractor(
- keyguardTransitionRepository,
- testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ repository = keyguardTransitionRepository,
+ scope = testScope.backgroundScope,
+ )
+ .keyguardTransitionInteractor,
)
}
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 22ac1b6..f811ce0 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
@@ -42,6 +42,7 @@
)
private val underTest =
PinBouncerViewModel(
+ applicationContext = context,
applicationScope = testScope.backgroundScope,
interactor =
utils.bouncerInteractor(
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 61432e2..608a187 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
@@ -67,6 +67,7 @@
)
private val underTest =
PinBouncerViewModel(
+ applicationContext = context,
applicationScope = testScope.backgroundScope,
interactor = bouncerInteractor,
isInputEnabled = MutableStateFlow(true).asStateFlow(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index b4bd473..925ac30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -10,7 +10,7 @@
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.shared.model.WakeSleepReason
@@ -67,10 +67,11 @@
resourceTrimmer =
ResourceTrimmer(
keyguardInteractor,
- KeyguardTransitionInteractor(
- keyguardTransitionRepository,
- testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor,
globalWindowManager,
testScope.backgroundScope,
testDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index e042564..d62db5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -49,7 +49,7 @@
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.AuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
@@ -216,7 +216,11 @@
)
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val keyguardTransitionInteractor =
- KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor
return DeviceEntryFaceAuthRepositoryImpl(
mContext,
fmOverride,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index ee5c1cc3..3e81cd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -89,7 +89,11 @@
faceAuthRepository = FakeDeviceEntryFaceAuthRepository()
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
keyguardTransitionInteractor =
- KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor
underTest =
SystemUIKeyguardFaceAuthInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index e9c22f9..0050d64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -292,10 +292,11 @@
appContext = mContext,
scope = testScope.backgroundScope,
transitionInteractor =
- KeyguardTransitionInteractor(
- keyguardTransitionRepository,
- testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = testScope.backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor,
repository = keyguardRepository,
logger = logger,
featureFlags =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index fa4941c..9e9c25e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -17,9 +17,9 @@
package com.android.systemui.keyguard.domain.interactor
-import com.android.systemui.RoboPilotTest
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.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -54,7 +54,10 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- underTest = KeyguardTransitionInteractor(repository, testScope.backgroundScope)
+ underTest = KeyguardTransitionInteractorFactory.create(
+ scope = testScope.backgroundScope,
+ repository = repository,
+ ).keyguardTransitionInteractor
}
@Test
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 b559015..d01a46e 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
@@ -19,6 +19,7 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
+import com.android.keyguard.TestScopeProvider
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.flags.FakeFeatureFlags
@@ -92,76 +93,97 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- testScope = TestScope()
+ testScope = TestScopeProvider.getTestScope()
keyguardRepository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
shadeRepository = FakeShadeRepository()
transitionRepository = spy(FakeKeyguardTransitionRepository())
- transitionInteractor = KeyguardTransitionInteractor(
- transitionRepository, testScope.backgroundScope)
whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
- fromLockscreenTransitionInteractor = FromLockscreenTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- shadeRepository = shadeRepository,
- ).apply { start() }
+ transitionInteractor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = testScope,
+ repository = transitionRepository,
+ )
+ .keyguardTransitionInteractor
- fromPrimaryBouncerTransitionInteractor = FromPrimaryBouncerTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- keyguardSecurityModel = keyguardSecurityModel,
- ).apply { start() }
+ fromLockscreenTransitionInteractor =
+ FromLockscreenTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ shadeRepository = shadeRepository,
+ )
+ .apply { start() }
- fromDreamingTransitionInteractor = FromDreamingTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
+ fromPrimaryBouncerTransitionInteractor =
+ FromPrimaryBouncerTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ keyguardSecurityModel = keyguardSecurityModel,
+ )
+ .apply { start() }
- fromAodTransitionInteractor = FromAodTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
+ fromDreamingTransitionInteractor =
+ FromDreamingTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
- fromGoneTransitionInteractor = FromGoneTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
+ fromAodTransitionInteractor =
+ FromAodTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
- fromDozingTransitionInteractor = FromDozingTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
+ fromGoneTransitionInteractor =
+ FromGoneTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
- fromOccludedTransitionInteractor = FromOccludedTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
+ fromDozingTransitionInteractor =
+ FromDozingTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
- fromAlternateBouncerTransitionInteractor = FromAlternateBouncerTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
+ fromOccludedTransitionInteractor =
+ FromOccludedTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
+
+ fromAlternateBouncerTransitionInteractor =
+ FromAlternateBouncerTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 08e99dc..6e7ba6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -46,7 +46,11 @@
private val fakeLightRevealScrimRepository = FakeLightRevealScrimRepository()
private val keyguardTransitionInteractor =
- KeyguardTransitionInteractor(fakeKeyguardTransitionRepository, TestScope().backgroundScope)
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = fakeKeyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor
private lateinit var underTest: LightRevealScrimInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt
new file mode 100644
index 0000000..2e97208
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import android.graphics.Point
+import android.view.ViewGroup
+import android.view.WindowManager
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.monet.utils.ArgbSubject.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+class DefaultLockscreenLayoutTest : SysuiTestCase() {
+ private lateinit var defaultLockscreenLayout: DefaultLockscreenLayout
+ private lateinit var rootView: KeyguardRootView
+ @Mock private lateinit var authController: AuthController
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ rootView = KeyguardRootView(context, null)
+ defaultLockscreenLayout =
+ DefaultLockscreenLayout(authController, keyguardUpdateMonitor, windowManager, context)
+ }
+
+ @Test
+ fun testLayoutViews_KeyguardIndicationArea() {
+ defaultLockscreenLayout.layoutViews(rootView)
+ val constraint = getViewConstraint(R.id.keyguard_indication_area)
+ assertThat(constraint.layout.bottomToBottom).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.mWidth).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT)
+ assertThat(constraint.layout.mHeight).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT)
+ }
+
+ @Test
+ fun testLayoutViews_lockIconView() {
+ defaultLockscreenLayout.layoutViews(rootView)
+ val constraint = getViewConstraint(R.id.lock_icon_view)
+ assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
+ }
+
+ @Test
+ fun testCenterLockIcon() {
+ defaultLockscreenLayout.centerLockIcon(Point(5, 6), 1F, 5, rootView)
+ val constraint = getViewConstraint(R.id.lock_icon_view)
+
+ assertThat(constraint.layout.mWidth).isEqualTo(2)
+ assertThat(constraint.layout.mHeight).isEqualTo(2)
+ assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.topMargin).isEqualTo(5)
+ assertThat(constraint.layout.startMargin).isEqualTo(4)
+ }
+
+ /** Get the ConstraintLayout constraint of the view. */
+ private fun getViewConstraint(viewId: Int): ConstraintSet.Constraint {
+ val constraintSet = ConstraintSet()
+ constraintSet.clone(rootView)
+ return constraintSet.getConstraint(viewId)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt
new file mode 100644
index 0000000..145b2fd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import java.io.PrintWriter
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+class KeyguardLayoutManagerCommandListenerTest : SysuiTestCase() {
+ private lateinit var keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener
+ @Mock private lateinit var commandRegistry: CommandRegistry
+ @Mock private lateinit var keyguardLayoutManager: KeyguardLayoutManager
+ @Mock private lateinit var pw: PrintWriter
+ private lateinit var command: () -> Command
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ keyguardLayoutManagerCommandListener =
+ KeyguardLayoutManagerCommandListener(
+ commandRegistry,
+ keyguardLayoutManager,
+ )
+ keyguardLayoutManagerCommandListener.start()
+ command =
+ withArgCaptor<() -> Command> {
+ verify(commandRegistry).registerCommand(eq("layout"), capture())
+ }
+ }
+
+ @Test
+ fun testHelp() {
+ command().execute(pw, listOf("help"))
+ verify(pw, atLeastOnce()).println(anyString())
+ verify(keyguardLayoutManager, never()).transitionToLayout(anyString())
+ }
+
+ @Test
+ fun testBlank() {
+ command().execute(pw, listOf())
+ verify(pw, atLeastOnce()).println(anyString())
+ verify(keyguardLayoutManager, never()).transitionToLayout(anyString())
+ }
+
+ @Test
+ fun testValidArg() {
+ bindFakeIdMapToLayoutManager()
+ command().execute(pw, listOf("fake"))
+ verify(keyguardLayoutManager).transitionToLayout("fake")
+ }
+
+ private fun bindFakeIdMapToLayoutManager() {
+ val map = mapOf("fake" to mock(LockscreenLayout::class.java))
+ whenever(keyguardLayoutManager.layoutIdMap).thenReturn(map)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt
new file mode 100644
index 0000000..95b2030
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.DefaultLockscreenLayout.Companion.DEFAULT
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+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.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+class KeyguardLayoutManagerTest : SysuiTestCase() {
+ private lateinit var keyguardLayoutManager: KeyguardLayoutManager
+ @Mock lateinit var configurationController: ConfigurationController
+ @Mock lateinit var defaultLockscreenLayout: DefaultLockscreenLayout
+ @Mock lateinit var keyguardRootView: KeyguardRootView
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(defaultLockscreenLayout.id).thenReturn(DEFAULT)
+ keyguardLayoutManager =
+ KeyguardLayoutManager(
+ configurationController,
+ setOf(defaultLockscreenLayout),
+ keyguardRootView
+ )
+ }
+
+ @Test
+ fun testDefaultLayout() {
+ keyguardLayoutManager.transitionToLayout(DEFAULT)
+ verify(defaultLockscreenLayout).layoutViews(keyguardRootView)
+ }
+
+ @Test
+ fun testTransitionToLayout_validId() {
+ assertThat(keyguardLayoutManager.transitionToLayout(DEFAULT)).isTrue()
+ }
+ @Test
+ fun testTransitionToLayout_invalidId() {
+ assertThat(keyguardLayoutManager.transitionToLayout("abc")).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index ab994b7..c67f535 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -22,6 +22,8 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
@@ -55,7 +57,12 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = DreamingToLockscreenTransitionViewModel(interactor, mock())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
index 694539b..75c8bff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
@@ -21,7 +21,7 @@
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -46,7 +46,12 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = GoneToDreamingTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 29886d5..d02b3fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -41,13 +41,12 @@
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
@@ -210,10 +209,10 @@
appContext = mContext,
scope = testScope.backgroundScope,
transitionInteractor =
- KeyguardTransitionInteractor(
- repository = FakeKeyguardTransitionRepository(),
- scope = testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ )
+ .keyguardTransitionInteractor,
repository = repository,
logger = UiEventLoggerFake(),
featureFlags = featureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index ea17751..12fe07f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -21,7 +21,7 @@
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -46,7 +46,12 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = LockscreenToDreamingTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index bf56a98..83ae631 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -21,7 +21,7 @@
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -46,7 +46,12 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = LockscreenToOccludedTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index 34da26e..8860399 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -21,7 +21,7 @@
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -46,7 +46,12 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = OccludedToLockscreenTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index f88b71d..d8c78eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -22,7 +22,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -55,7 +55,12 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest =
PrimaryBouncerToGoneTransitionViewModel(
interactor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
index b40ebc9..91b0245 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
@@ -193,6 +193,17 @@
}
@Test
+ fun dozeWakeUpAnimationWaiting_inSplitShade_mediaIsHidden() {
+ val splitShadeContainer = FrameLayout(context)
+ keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
+ keyguardMediaController.useSplitShade = true
+
+ keyguardMediaController.isDozeWakeUpAnimationWaiting = true
+
+ assertThat(splitShadeContainer.visibility).isEqualTo(GONE)
+ }
+
+ @Test
fun dozing_inSingleShade_mediaIsVisible() {
val splitShadeContainer = FrameLayout(context)
keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
@@ -203,6 +214,17 @@
assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE)
}
+ @Test
+ fun dozeWakeUpAnimationWaiting_inSingleShade_mediaIsVisible() {
+ val splitShadeContainer = FrameLayout(context)
+ keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
+ keyguardMediaController.useSplitShade = false
+
+ keyguardMediaController.isDozeWakeUpAnimationWaiting = true
+
+ assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE)
+ }
+
private fun setDozing() {
whenever(statusBarStateController.isDozing).thenReturn(true)
statusBarStateListener.onDozingChanged(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
index 9e54224..5890cbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
@@ -25,7 +25,7 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -97,10 +97,11 @@
multiShadeInteractor = interactor,
featureFlags = featureFlags,
keyguardTransitionInteractor =
- KeyguardTransitionInteractor(
- repository = keyguardTransitionRepository,
- scope = testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor,
falsingManager = falsingManager,
shadeController = shadeController,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index ef7c7bc..9188293 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -80,6 +80,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.common.ui.view.LongPressHandlingView;
@@ -91,7 +92,6 @@
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewConfigurator;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -629,7 +629,7 @@
mHeadsUpManager);
mNotificationPanelViewController.setTrackingStartedListener(() -> {});
mNotificationPanelViewController.setOpenCloseListener(
- new NotificationPanelViewController.OpenCloseListener() {
+ new OpenCloseListener() {
@Override
public void onClosingFinished() {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 5802eb3..eb4ae1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -514,6 +514,17 @@
}
@Test
+ public void keyguardStatusView_willPlayDelayedDoze_notifiesKeyguardMediaController() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(true);
+
+ verify(mKeyguardMediaController).setDozeWakeUpAnimationWaiting(true);
+ }
+
+ @Test
public void keyguardStatusView_willPlayDelayedDoze_isCentered_thenStillCenteredIfNoNotifs() {
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
mStatusBarStateController.setState(KEYGUARD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 2a9b403..5fb3a79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -28,6 +28,11 @@
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
+import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository
+import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
+import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
+import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.dock.DockManager
@@ -35,14 +40,9 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
-import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
-import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
-import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
@@ -80,9 +80,9 @@
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import java.util.Optional
+import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -198,10 +198,9 @@
multiShadeInteractor = multiShadeInteractor,
featureFlags = featureFlags,
keyguardTransitionInteractor =
- KeyguardTransitionInteractor(
- repository = FakeKeyguardTransitionRepository(),
- scope = testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ ).keyguardTransitionInteractor,
falsingManager = FalsingManagerFake(),
shadeController = shadeController,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 252a03b..544137e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -40,8 +40,8 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
@@ -211,10 +211,10 @@
multiShadeInteractor = multiShadeInteractor,
featureFlags = featureFlags,
keyguardTransitionInteractor =
- KeyguardTransitionInteractor(
- repository = FakeKeyguardTransitionRepository(),
- scope = testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ )
+ .keyguardTransitionInteractor,
falsingManager = FalsingManagerFake(),
shadeController = shadeController,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index 00a0567..729c4a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -56,7 +56,7 @@
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var assistManager: AssistManager
@Mock private lateinit var gutsManager: NotificationGutsManager
- @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
+ @Mock private lateinit var shadeViewController: ShadeViewController
@Mock private lateinit var nswvc: NotificationShadeWindowViewController
@Mock private lateinit var display: Display
@@ -82,7 +82,7 @@
Lazy { gutsManager },
)
shadeController.setNotificationShadeWindowViewController(nswvc)
- shadeController.setNotificationPanelViewController(notificationPanelViewController)
+ shadeController.setShadeViewController(shadeViewController)
}
@Test
@@ -91,9 +91,9 @@
// Trying to open it does nothing.
shadeController.animateExpandShade()
- verify(notificationPanelViewController, never()).expandToNotifications()
+ verify(shadeViewController, never()).expandToNotifications()
shadeController.animateExpandQs()
- verify(notificationPanelViewController, never()).expand(ArgumentMatchers.anyBoolean())
+ verify(shadeViewController, never()).expand(ArgumentMatchers.anyBoolean())
}
@Test
@@ -102,15 +102,15 @@
// Can now be opened.
shadeController.animateExpandShade()
- verify(notificationPanelViewController).expandToNotifications()
+ verify(shadeViewController).expandToNotifications()
shadeController.animateExpandQs()
- verify(notificationPanelViewController).expandToQs()
+ verify(shadeViewController).expandToQs()
}
@Test
fun cancelExpansionAndCollapseShade_callsCancelCurrentTouch() {
// GIVEN the shade is tracking a touch
- whenever(notificationPanelViewController.isTracking).thenReturn(true)
+ whenever(shadeViewController.isTracking).thenReturn(true)
// WHEN cancelExpansionAndCollapseShade is called
shadeController.cancelExpansionAndCollapseShade()
@@ -122,7 +122,7 @@
@Test
fun cancelExpansionAndCollapseShade_doesNotCallAnimateCollapseShade_whenCollapsed() {
// GIVEN the shade is tracking a touch
- whenever(notificationPanelViewController.isTracking).thenReturn(false)
+ whenever(shadeViewController.isTracking).thenReturn(false)
// WHEN cancelExpansionAndCollapseShade is called
shadeController.cancelExpansionAndCollapseShade()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index 4524797..3ea8f54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -84,6 +84,7 @@
MockitoAnnotations.initMocks(this)
featureFlags.set(Flags.FACE_AUTH_REFACTOR, false)
+ featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val refreshUsersScheduler =
RefreshUsersScheduler(
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 1ffffe4..88d8dfc 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
@@ -450,7 +450,7 @@
() -> mAssistManager,
() -> mNotificationGutsManager
));
- mShadeController.setNotificationPanelViewController(mNotificationPanelViewController);
+ mShadeController.setShadeViewController(mNotificationPanelViewController);
mShadeController.setNotificationShadeWindowViewController(
mNotificationShadeWindowViewController);
mShadeController.setNotificationPresenter(mNotificationPresenter);
@@ -490,9 +490,11 @@
mMetricsLogger,
mShadeLogger,
mUiBgExecutor,
+ mNotificationPanelViewController,
mNotificationMediaManager,
mLockscreenUserManager,
mRemoteInputManager,
+ mQuickSettingsController,
mUserSwitcherController,
mBatteryController,
mColorExtractor,
@@ -587,8 +589,6 @@
// TODO: we should be able to call mCentralSurfaces.start() and have all the below values
// initialized automatically and make NPVC private.
mCentralSurfaces.mNotificationShadeWindowView = mNotificationShadeWindowView;
- mCentralSurfaces.mShadeSurface = mNotificationPanelViewController;
- mCentralSurfaces.mQsController = mQuickSettingsController;
mCentralSurfaces.mDozeScrimController = mDozeScrimController;
mCentralSurfaces.mPresenter = mNotificationPresenter;
mCentralSurfaces.mKeyguardIndicationController = mKeyguardIndicationController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenWallpaperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenWallpaperTest.kt
new file mode 100644
index 0000000..47671fb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenWallpaperTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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
+
+import android.app.WallpaperManager
+import android.content.pm.UserInfo
+import android.os.Looper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.user.data.model.SelectionStatus
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.utils.os.FakeHandler
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+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.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.verify
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class LockscreenWallpaperTest : SysuiTestCase() {
+
+ private lateinit var underTest: LockscreenWallpaper
+
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val userRepository = FakeUserRepository()
+
+ private val wallpaperManager: WallpaperManager = mock()
+
+ @Before
+ fun setUp() {
+ whenever(wallpaperManager.isLockscreenLiveWallpaperEnabled).thenReturn(false)
+ whenever(wallpaperManager.isWallpaperSupported).thenReturn(true)
+ underTest =
+ LockscreenWallpaper(
+ /* wallpaperManager= */ wallpaperManager,
+ /* iWallpaperManager= */ mock(),
+ /* keyguardUpdateMonitor= */ mock(),
+ /* dumpManager= */ mock(),
+ /* mediaManager= */ mock(),
+ /* mainHandler= */ FakeHandler(Looper.getMainLooper()),
+ /* javaAdapter= */ JavaAdapter(testScope.backgroundScope),
+ /* userRepository= */ userRepository,
+ /* userTracker= */ mock(),
+ )
+ underTest.start()
+ }
+
+ @Test
+ fun getBitmap_matchesUserIdFromUserRepo() =
+ testScope.runTest {
+ val info = UserInfo(/* id= */ 5, /* name= */ "id5", /* flags= */ 0)
+ userRepository.setUserInfos(listOf(info))
+ userRepository.setSelectedUserInfo(info)
+
+ underTest.bitmap
+
+ verify(wallpaperManager).getWallpaperFile(any(), eq(5))
+ }
+
+ @Test
+ fun getBitmap_usesOldUserIfNewUserInProgress() =
+ testScope.runTest {
+ val info5 = UserInfo(/* id= */ 5, /* name= */ "id5", /* flags= */ 0)
+ val info6 = UserInfo(/* id= */ 6, /* name= */ "id6", /* flags= */ 0)
+ userRepository.setUserInfos(listOf(info5, info6))
+ userRepository.setSelectedUserInfo(info5)
+
+ // WHEN the selection of user 6 is only in progress
+ userRepository.setSelectedUserInfo(
+ info6,
+ selectionStatus = SelectionStatus.SELECTION_IN_PROGRESS
+ )
+
+ underTest.bitmap
+
+ // THEN we still use user 5 for wallpaper selection
+ verify(wallpaperManager).getWallpaperFile(any(), eq(5))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index 6301fa0..842d548 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -20,7 +20,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -48,7 +48,11 @@
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val interactor =
- KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor
underTest = CollapsedStatusBarViewModelImpl(interactor, testScope.backgroundScope)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 079fbcd..0c28cbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -26,6 +26,8 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.user.data.model.SelectedUserModel
+import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
@@ -224,6 +226,40 @@
}
@Test
+ fun userTrackerCallback_updatesSelectionStatus() = runSelfCancelingTest {
+ underTest = create(this)
+ var selectedUser: SelectedUserModel? = null
+ underTest.selectedUser.onEach { selectedUser = it }.launchIn(this)
+ setUpUsers(count = 2, selectedIndex = 1)
+
+ // WHEN the user is changing
+ tracker.onUserChanging(userId = 1)
+
+ // THEN the selection status is IN_PROGRESS
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+
+ // WHEN the user has finished changing
+ tracker.onUserChanged(userId = 1)
+
+ // THEN the selection status is COMPLETE
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
+
+ tracker.onProfileChanged()
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
+
+ setUpUsers(count = 2, selectedIndex = 0)
+
+ tracker.onUserChanging(userId = 0)
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+
+ // WHEN a profile change occurs while a user is changing
+ tracker.onProfileChanged()
+
+ // THEN the selection status remains as IN_PROGRESS
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+ }
+
+ @Test
fun userSwitchingInProgress_registersUserTrackerCallback() = runSelfCancelingTest {
underTest = create(this)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt
new file mode 100644
index 0000000..312ade5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import kotlinx.coroutines.CoroutineScope
+
+/**
+ * Helper to create a new KeyguardTransitionInteractor in a way that doesn't require modifying 20+
+ * tests whenever we add a constructor param.
+ */
+object KeyguardTransitionInteractorFactory {
+ @JvmOverloads
+ @JvmStatic
+ fun create(
+ scope: CoroutineScope,
+ repository: KeyguardTransitionRepository = FakeKeyguardTransitionRepository(),
+ ): WithDependencies {
+ return WithDependencies(
+ repository = repository,
+ KeyguardTransitionInteractor(
+ scope = scope,
+ repository = repository,
+ )
+ )
+ }
+
+ data class WithDependencies(
+ val repository: KeyguardTransitionRepository,
+ val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index 61e5b5f..51ee0c0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -19,18 +19,28 @@
import android.content.pm.UserInfo
import android.os.UserHandle
+import com.android.systemui.user.data.model.SelectedUserModel
+import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.yield
class FakeUserRepository : UserRepository {
companion object {
// User id to represent a non system (human) user id. We presume this is the main user.
private const val MAIN_USER_ID = 10
+
+ private val DEFAULT_SELECTED_USER = 0
+ private val DEFAULT_SELECTED_USER_INFO =
+ UserInfo(
+ /* id= */ DEFAULT_SELECTED_USER,
+ /* name= */ "default selected user",
+ /* flags= */ 0,
+ )
}
private val _userSwitcherSettings = MutableStateFlow(UserSwitcherSettingsModel())
@@ -40,8 +50,11 @@
private val _userInfos = MutableStateFlow<List<UserInfo>>(emptyList())
override val userInfos: Flow<List<UserInfo>> = _userInfos.asStateFlow()
- private val _selectedUserInfo = MutableStateFlow<UserInfo?>(null)
- override val selectedUserInfo: Flow<UserInfo> = _selectedUserInfo.filterNotNull()
+ override val selectedUser =
+ MutableStateFlow(
+ SelectedUserModel(DEFAULT_SELECTED_USER_INFO, SelectionStatus.SELECTION_COMPLETE)
+ )
+ override val selectedUserInfo: Flow<UserInfo> = selectedUser.map { it.userInfo }
private val _userSwitchingInProgress = MutableStateFlow(false)
override val userSwitchingInProgress: Flow<Boolean>
@@ -72,7 +85,7 @@
}
override fun getSelectedUserInfo(): UserInfo {
- return checkNotNull(_selectedUserInfo.value)
+ return selectedUser.value.userInfo
}
override fun isSimpleUserSwitcher(): Boolean {
@@ -87,12 +100,15 @@
_userInfos.value = infos
}
- suspend fun setSelectedUserInfo(userInfo: UserInfo) {
+ suspend fun setSelectedUserInfo(
+ userInfo: UserInfo,
+ selectionStatus: SelectionStatus = SelectionStatus.SELECTION_COMPLETE,
+ ) {
check(_userInfos.value.contains(userInfo)) {
"Cannot select the following user, it is not in the list of user infos: $userInfo!"
}
- _selectedUserInfo.value = userInfo
+ selectedUser.value = SelectedUserModel(userInfo, selectionStatus)
yield()
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index dae9c53..d426c79 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1994,7 +1994,7 @@
}
private void dismissUserSwitchDialog(Runnable onDismissed) {
- mInjector.dismissUserSwitchingDialog(onDismissed);
+ mUiHandler.post(() -> mInjector.dismissUserSwitchingDialog(onDismissed));
}
private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6b69e1c..63bb026 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -22,7 +22,6 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
@@ -280,15 +279,21 @@
private static final int VPN_DEFAULT_SCORE = 101;
/**
- * The reset session timer for data stall. If a session has not successfully revalidated after
- * the delay, the session will be torn down and restarted in an attempt to recover. Delay
+ * The recovery timer for data stall. If a session has not successfully revalidated after
+ * the delay, the session will perform MOBIKE or be restarted in an attempt to recover. Delay
* counter is reset on successful validation only.
*
+ * <p>The first {@code MOBIKE_RECOVERY_ATTEMPT} timers are used for performing MOBIKE.
+ * System will perform session reset for the remaining timers.
* <p>If retries have exceeded the length of this array, the last entry in the array will be
* used as a repeating interval.
*/
- private static final long[] DATA_STALL_RESET_DELAYS_SEC = {30L, 60L, 120L, 240L, 480L, 960L};
-
+ private static final long[] DATA_STALL_RECOVERY_DELAYS_MS =
+ {1000L, 5000L, 30000L, 60000L, 120000L, 240000L, 480000L, 960000L};
+ /**
+ * Maximum attempts to perform MOBIKE when the network is bad.
+ */
+ private static final int MAX_MOBIKE_RECOVERY_ATTEMPT = 2;
/**
* The initial token value of IKE session.
*/
@@ -380,6 +385,7 @@
private final INetworkManagementService mNms;
private final INetd mNetd;
@VisibleForTesting
+ @GuardedBy("this")
protected VpnConfig mConfig;
private final NetworkProvider mNetworkProvider;
@VisibleForTesting
@@ -392,7 +398,6 @@
private final UserManager mUserManager;
private final VpnProfileStore mVpnProfileStore;
- protected boolean mDataStallSuspected = false;
@VisibleForTesting
VpnProfileStore getVpnProfileStore() {
@@ -685,14 +690,14 @@
}
/**
- * Get the length of time to wait before resetting the ike session when a data stall is
- * suspected.
+ * Get the length of time to wait before perform data stall recovery when the validation
+ * result is bad.
*/
- public long getDataStallResetSessionSeconds(int count) {
- if (count >= DATA_STALL_RESET_DELAYS_SEC.length) {
- return DATA_STALL_RESET_DELAYS_SEC[DATA_STALL_RESET_DELAYS_SEC.length - 1];
+ public long getValidationFailRecoveryMs(int count) {
+ if (count >= DATA_STALL_RECOVERY_DELAYS_MS.length) {
+ return DATA_STALL_RECOVERY_DELAYS_MS[DATA_STALL_RECOVERY_DELAYS_MS.length - 1];
} else {
- return DATA_STALL_RESET_DELAYS_SEC[count];
+ return DATA_STALL_RECOVERY_DELAYS_MS[count];
}
}
@@ -1598,6 +1603,8 @@
return network;
}
+ // TODO : this is not synchronized(this) but reads from mConfig, which is dangerous
+ // This file makes an effort to avoid partly initializing mConfig, but this is still not great
private LinkProperties makeLinkProperties() {
// The design of disabling IPv6 is only enabled for IKEv2 VPN because it needs additional
// logic to handle IPv6 only VPN, and the IPv6 only VPN may be restarted when its MTU
@@ -1679,6 +1686,7 @@
* registering a new NetworkAgent. This is not always possible if the new VPN configuration
* has certain changes, in which case this method would just return {@code false}.
*/
+ // TODO : this method is not synchronized(this) but reads from mConfig
private boolean updateLinkPropertiesInPlaceIfPossible(NetworkAgent agent, VpnConfig oldConfig) {
// NetworkAgentConfig cannot be updated without registering a new NetworkAgent.
// Strictly speaking, bypassability is affected by lockdown and therefore it's possible
@@ -2269,7 +2277,12 @@
*/
public synchronized VpnConfig getVpnConfig() {
enforceControlPermission();
- return mConfig;
+ // Constructor of VpnConfig cannot take a null parameter. Return null directly if mConfig is
+ // null
+ if (mConfig == null) return null;
+ // mConfig is guarded by "this" and can be modified by another thread as soon as
+ // this method returns, so this method must return a copy.
+ return new VpnConfig(mConfig);
}
@Deprecated
@@ -2315,6 +2328,7 @@
}
};
+ @GuardedBy("this")
private void cleanupVpnStateLocked() {
mStatusIntent = null;
resetNetworkCapabilities();
@@ -2837,9 +2851,7 @@
}
final boolean isLegacyVpn = mVpnRunner instanceof LegacyVpnRunner;
-
mVpnRunner.exit();
- mVpnRunner = null;
// LegacyVpn uses daemons that must be shut down before new ones are brought up.
// The same limitation does not apply to Platform VPNs.
@@ -3044,7 +3056,6 @@
@Nullable private IkeSessionWrapper mSession;
@Nullable private IkeSessionConnectionInfo mIkeConnectionInfo;
- @Nullable private VpnConnectivityDiagnosticsCallback mDiagnosticsCallback;
// mMobikeEnabled can only be updated after IKE AUTH is finished.
private boolean mMobikeEnabled = false;
@@ -3055,7 +3066,7 @@
* <p>This variable controls the retry delay, and is reset when the VPN pass network
* validation.
*/
- private int mDataStallRetryCount = 0;
+ private int mValidationFailRetryCount = 0;
/**
* The number of attempts since the last successful connection.
@@ -3084,6 +3095,7 @@
}
};
+ // GuardedBy("Vpn.this") (annotation can't be applied to constructor)
IkeV2VpnRunner(
@NonNull Ikev2VpnProfile profile, @NonNull ScheduledThreadPoolExecutor executor) {
super(TAG);
@@ -3136,15 +3148,6 @@
mConnectivityManager.registerSystemDefaultNetworkCallback(mNetworkCallback,
new Handler(mLooper));
}
-
- // DiagnosticsCallback may return more than one alive VPNs, but VPN will filter based on
- // Network object.
- final NetworkRequest diagRequest = new NetworkRequest.Builder()
- .addTransportType(TRANSPORT_VPN)
- .removeCapability(NET_CAPABILITY_NOT_VPN).build();
- mDiagnosticsCallback = new VpnConnectivityDiagnosticsCallback();
- mConnectivityDiagnosticsManager.registerConnectivityDiagnosticsCallback(
- diagRequest, mExecutor, mDiagnosticsCallback);
}
private boolean isActiveNetwork(@Nullable Network network) {
@@ -3710,11 +3713,14 @@
}
public void updateVpnTransportInfoAndNetCap(int keepaliveDelaySec) {
- final VpnTransportInfo info = new VpnTransportInfo(
- getActiveVpnType(),
- mConfig.session,
- mConfig.allowBypass && !mLockdown,
- areLongLivedTcpConnectionsExpensive(keepaliveDelaySec));
+ final VpnTransportInfo info;
+ synchronized (Vpn.this) {
+ info = new VpnTransportInfo(
+ getActiveVpnType(),
+ mConfig.session,
+ mConfig.allowBypass && !mLockdown,
+ areLongLivedTcpConnectionsExpensive(keepaliveDelaySec));
+ }
final boolean ncUpdateRequired = !info.equals(mNetworkCapabilities.getTransportInfo());
if (ncUpdateRequired) {
mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities)
@@ -3875,39 +3881,12 @@
}
}
- class VpnConnectivityDiagnosticsCallback
- extends ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
- // The callback runs in the executor thread.
- @Override
- public void onDataStallSuspected(
- ConnectivityDiagnosticsManager.DataStallReport report) {
- synchronized (Vpn.this) {
- // Ignore stale runner.
- if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
-
- // Handle the report only for current VPN network. If data stall is already
- // reported, ignoring the other reports. It means that the stall is not
- // recovered by MOBIKE and should be on the way to reset the ike session.
- if (mNetworkAgent != null
- && mNetworkAgent.getNetwork().equals(report.getNetwork())
- && !mDataStallSuspected) {
- Log.d(TAG, "Data stall suspected");
-
- // Trigger MOBIKE.
- maybeMigrateIkeSessionAndUpdateVpnTransportInfo(mActiveNetwork);
- mDataStallSuspected = true;
- }
- }
- }
- }
-
public void onValidationStatus(int status) {
mEventChanges.log("[Validation] validation status " + status);
if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
// No data stall now. Reset it.
mExecutor.execute(() -> {
- mDataStallSuspected = false;
- mDataStallRetryCount = 0;
+ mValidationFailRetryCount = 0;
if (mScheduledHandleDataStallFuture != null) {
Log.d(TAG, "Recovered from stall. Cancel pending reset action.");
mScheduledHandleDataStallFuture.cancel(false /* mayInterruptIfRunning */);
@@ -3918,8 +3897,21 @@
// Skip other invalid status if the scheduled recovery exists.
if (mScheduledHandleDataStallFuture != null) return;
+ if (mValidationFailRetryCount < MAX_MOBIKE_RECOVERY_ATTEMPT) {
+ Log.d(TAG, "Validation failed");
+
+ // Trigger MOBIKE to recover first.
+ mExecutor.schedule(() -> {
+ maybeMigrateIkeSessionAndUpdateVpnTransportInfo(mActiveNetwork);
+ }, mDeps.getValidationFailRecoveryMs(mValidationFailRetryCount++),
+ TimeUnit.MILLISECONDS);
+ return;
+ }
+
+ // Data stall is not recovered by MOBIKE. Try to reset session to recover it.
mScheduledHandleDataStallFuture = mExecutor.schedule(() -> {
- if (mDataStallSuspected) {
+ // Only perform the recovery when the network is still bad.
+ if (mValidationFailRetryCount > 0) {
Log.d(TAG, "Reset session to recover stalled network");
// This will reset old state if it exists.
startIkeSession(mActiveNetwork);
@@ -3928,7 +3920,9 @@
// Reset mScheduledHandleDataStallFuture since it's already run on executor
// thread.
mScheduledHandleDataStallFuture = null;
- }, mDeps.getDataStallResetSessionSeconds(mDataStallRetryCount++), TimeUnit.SECONDS);
+ // TODO: compute the delay based on the last recovery timestamp
+ }, mDeps.getValidationFailRecoveryMs(mValidationFailRetryCount++),
+ TimeUnit.MILLISECONDS);
}
}
@@ -4220,7 +4214,7 @@
* consistency of the Ikev2VpnRunner fields.
*/
private void disconnectVpnRunner() {
- mEventChanges.log("[VPNRunner] Disconnect runner, underlying network" + mActiveNetwork);
+ mEventChanges.log("[VPNRunner] Disconnect runner, underlying net " + mActiveNetwork);
mActiveNetwork = null;
mUnderlyingNetworkCapabilities = null;
mUnderlyingLinkProperties = null;
@@ -4231,8 +4225,6 @@
mCarrierConfigManager.unregisterCarrierConfigChangeListener(
mCarrierConfigChangeListener);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
- mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
- mDiagnosticsCallback);
clearVpnNetworkPreference(mSessionKey);
mExecutor.shutdown();
@@ -4293,6 +4285,7 @@
}
};
+ // GuardedBy("Vpn.this") (annotation can't be applied to constructor)
LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd, VpnProfile profile) {
super(TAG);
if (racoon == null && mtpd == null) {
@@ -4500,46 +4493,46 @@
}
// Set the interface and the addresses in the config.
- mConfig.interfaze = parameters[0].trim();
-
- mConfig.addLegacyAddresses(parameters[1]);
- // Set the routes if they are not set in the config.
- if (mConfig.routes == null || mConfig.routes.isEmpty()) {
- mConfig.addLegacyRoutes(parameters[2]);
- }
-
- // Set the DNS servers if they are not set in the config.
- if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
- String dnsServers = parameters[3].trim();
- if (!dnsServers.isEmpty()) {
- mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
- }
- }
-
- // Set the search domains if they are not set in the config.
- if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
- String searchDomains = parameters[4].trim();
- if (!searchDomains.isEmpty()) {
- mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
- }
- }
-
- // Add a throw route for the VPN server endpoint, if one was specified.
- if (endpointAddress instanceof Inet4Address) {
- mConfig.routes.add(new RouteInfo(
- new IpPrefix(endpointAddress, 32), null /*gateway*/,
- null /*iface*/, RTN_THROW));
- } else if (endpointAddress instanceof Inet6Address) {
- mConfig.routes.add(new RouteInfo(
- new IpPrefix(endpointAddress, 128), null /*gateway*/,
- null /*iface*/, RTN_THROW));
- } else {
- Log.e(TAG, "Unknown IP address family for VPN endpoint: "
- + endpointAddress);
- }
-
- // Here is the last step and it must be done synchronously.
synchronized (Vpn.this) {
+ mConfig.interfaze = parameters[0].trim();
+
+ mConfig.addLegacyAddresses(parameters[1]);
+ // Set the routes if they are not set in the config.
+ if (mConfig.routes == null || mConfig.routes.isEmpty()) {
+ mConfig.addLegacyRoutes(parameters[2]);
+ }
+
+ // Set the DNS servers if they are not set in the config.
+ if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
+ String dnsServers = parameters[3].trim();
+ if (!dnsServers.isEmpty()) {
+ mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
+ }
+ }
+
+ // Set the search domains if they are not set in the config.
+ if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
+ String searchDomains = parameters[4].trim();
+ if (!searchDomains.isEmpty()) {
+ mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
+ }
+ }
+
+ // Add a throw route for the VPN server endpoint, if one was specified.
+ if (endpointAddress instanceof Inet4Address) {
+ mConfig.routes.add(new RouteInfo(
+ new IpPrefix(endpointAddress, 32), null /*gateway*/,
+ null /*iface*/, RTN_THROW));
+ } else if (endpointAddress instanceof Inet6Address) {
+ mConfig.routes.add(new RouteInfo(
+ new IpPrefix(endpointAddress, 128), null /*gateway*/,
+ null /*iface*/, RTN_THROW));
+ } else {
+ Log.e(TAG, "Unknown IP address family for VPN endpoint: "
+ + endpointAddress);
+ }
+
+ // Here is the last step and it must be done synchronously.
// Set the start time
mConfig.startTime = SystemClock.elapsedRealtime();
@@ -4773,25 +4766,26 @@
try {
// Build basic config
- mConfig = new VpnConfig();
+ final VpnConfig config = new VpnConfig();
if (VpnConfig.LEGACY_VPN.equals(packageName)) {
- mConfig.legacy = true;
- mConfig.session = profile.name;
- mConfig.user = profile.key;
+ config.legacy = true;
+ config.session = profile.name;
+ config.user = profile.key;
// TODO: Add support for configuring meteredness via Settings. Until then, use a
// safe default.
- mConfig.isMetered = true;
+ config.isMetered = true;
} else {
- mConfig.user = packageName;
- mConfig.isMetered = profile.isMetered;
+ config.user = packageName;
+ config.isMetered = profile.isMetered;
}
- mConfig.startTime = SystemClock.elapsedRealtime();
- mConfig.proxyInfo = profile.proxy;
- mConfig.requiresInternetValidation = profile.requiresInternetValidation;
- mConfig.excludeLocalRoutes = profile.excludeLocalRoutes;
- mConfig.allowBypass = profile.isBypassable;
- mConfig.disallowedApplications = getAppExclusionList(mPackage);
+ config.startTime = SystemClock.elapsedRealtime();
+ config.proxyInfo = profile.proxy;
+ config.requiresInternetValidation = profile.requiresInternetValidation;
+ config.excludeLocalRoutes = profile.excludeLocalRoutes;
+ config.allowBypass = profile.isBypassable;
+ config.disallowedApplications = getAppExclusionList(mPackage);
+ mConfig = config;
switch (profile.type) {
case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
@@ -4805,6 +4799,7 @@
mVpnRunner.start();
break;
default:
+ mConfig = null;
updateState(DetailedState.FAILED, "Invalid platform VPN type");
Log.d(TAG, "Unknown VPN profile type: " + profile.type);
break;
@@ -5216,7 +5211,7 @@
pw.println("MOBIKE " + (runner.mMobikeEnabled ? "enabled" : "disabled"));
pw.println("Profile: " + runner.mProfile);
pw.println("Token: " + runner.mCurrentToken);
- if (mDataStallSuspected) pw.println("Data stall suspected");
+ pw.println("Validation failed retry count:" + runner.mValidationFailRetryCount);
if (runner.mScheduledHandleDataStallFuture != null) {
pw.println("Reset session scheduled");
}
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 47cde15..5b11cfe 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -18,29 +18,42 @@
import android.hardware.display.BrightnessInfo;
import android.os.IBinder;
+import android.provider.DeviceConfigInterface;
+
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import java.io.PrintWriter;
import java.util.function.BooleanSupplier;
class BrightnessRangeController {
- private static final boolean NBM_FEATURE_FLAG = false;
-
private final HighBrightnessModeController mHbmController;
private final NormalBrightnessModeController mNormalBrightnessModeController =
new NormalBrightnessModeController();
private final Runnable mModeChangeCallback;
+ private final boolean mUseNbmController;
+
BrightnessRangeController(HighBrightnessModeController hbmController,
Runnable modeChangeCallback) {
- mHbmController = hbmController;
- mModeChangeCallback = modeChangeCallback;
+ this(hbmController, modeChangeCallback,
+ new DeviceConfigParameterProvider(DeviceConfigInterface.REAL));
}
+ BrightnessRangeController(HighBrightnessModeController hbmController,
+ Runnable modeChangeCallback, DeviceConfigParameterProvider configParameterProvider) {
+ mHbmController = hbmController;
+ mModeChangeCallback = modeChangeCallback;
+ mUseNbmController = configParameterProvider.isNormalBrightnessControllerFeatureEnabled();
+ }
void dump(PrintWriter pw) {
+ pw.println("BrightnessRangeController:");
+ pw.println(" mUseNormalBrightnessController=" + mUseNbmController);
mHbmController.dump(pw);
+ mNormalBrightnessModeController.dump(pw);
+
}
void onAmbientLuxChange(float ambientLux) {
@@ -90,7 +103,7 @@
float getCurrentBrightnessMax() {
- if (NBM_FEATURE_FLAG && mHbmController.getHighBrightnessMode()
+ if (mUseNbmController && mHbmController.getHighBrightnessMode()
== BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF) {
return Math.min(mHbmController.getCurrentBrightnessMax(),
mNormalBrightnessModeController.getCurrentBrightnessMax());
@@ -111,7 +124,7 @@
}
private void applyChanges(BooleanSupplier nbmChangesFunc, Runnable hbmChangesFunc) {
- if (NBM_FEATURE_FLAG) {
+ if (mUseNbmController) {
boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean();
hbmChangesFunc.run();
// if nbm transition changed - trigger callback
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index cfdcd63..c421ec0 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -22,7 +22,6 @@
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.BrightnessInfo;
-import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IThermalEventListener;
@@ -38,6 +37,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -63,7 +63,7 @@
private final Runnable mThrottlingChangeCallback;
private final SkinThermalStatusObserver mSkinThermalStatusObserver;
private final DeviceConfigListener mDeviceConfigListener;
- private final DeviceConfigInterface mDeviceConfig;
+ private final DeviceConfigParameterProvider mConfigParameterProvider;
private int mThrottlingStatus;
@@ -118,7 +118,7 @@
mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
mUniqueDisplayId = uniqueDisplayId;
- mDeviceConfig = injector.getDeviceConfig();
+ mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig());
mDeviceConfigListener = new DeviceConfigListener();
mThermalBrightnessThrottlingDataId = throttlingDataId;
mDdcThermalThrottlingDataMap = thermalBrightnessThrottlingDataMap;
@@ -145,7 +145,7 @@
void stop() {
mSkinThermalStatusObserver.stopObserving();
- mDeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigListener);
+ mConfigParameterProvider.removeOnPropertiesChangedListener(mDeviceConfigListener);
// We're asked to stop throttling, so reset brightness restrictions.
mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
mBrightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
@@ -248,12 +248,6 @@
mSkinThermalStatusObserver.dump(pw);
}
- private String getThermalBrightnessThrottlingDataString() {
- return mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA,
- /* defaultValue= */ null);
- }
-
// The brightness throttling data id may or may not be specified in the string that is passed
// in, if there is none specified, we assume it is for the default case. Each string passed in
// here must be for one display and one throttling id.
@@ -318,7 +312,8 @@
private void loadThermalBrightnessThrottlingDataFromDeviceConfig() {
HashMap<String, HashMap<String, ThermalBrightnessThrottlingData>> tempThrottlingData =
new HashMap<>(1);
- mThermalBrightnessThrottlingDataString = getThermalBrightnessThrottlingDataString();
+ mThermalBrightnessThrottlingDataString =
+ mConfigParameterProvider.getBrightnessThrottlingData();
boolean validConfig = true;
mThermalBrightnessThrottlingDataOverride.clear();
if (mThermalBrightnessThrottlingDataString != null) {
@@ -390,8 +385,7 @@
public Executor mExecutor = new HandlerExecutor(mDeviceConfigHandler);
public void startListening() {
- mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- mExecutor, this);
+ mConfigParameterProvider.addOnPropertiesChangedListener(mExecutor, this);
}
@Override
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 7e8771e..1a8e2db 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -24,7 +24,6 @@
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.hardware.display.DisplayManager.EventsMask;
-import static android.hardware.display.DisplayManager.HDR_OUTPUT_CONTROL_FLAG;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
@@ -42,7 +41,6 @@
import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_UNSUPPORTED;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.ROOT_UID;
-import static android.provider.DeviceConfig.NAMESPACE_DISPLAY_MANAGER;
import android.Manifest;
import android.annotation.NonNull;
@@ -116,7 +114,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -152,6 +150,7 @@
import com.android.server.UiThread;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayDeviceConfig.SensorData;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.layout.Layout;
import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.display.utils.SensorUtils;
@@ -506,6 +505,8 @@
private final BrightnessSynchronizer mBrightnessSynchronizer;
+ private final DeviceConfigParameterProvider mConfigParameterProvider;
+
/**
* Applications use {@link android.view.Display#getRefreshRate} and
* {@link android.view.Display.Mode#getRefreshRate} to know what is the display refresh rate.
@@ -558,6 +559,7 @@
mWideColorSpace = colorSpaces[1];
mOverlayProperties = SurfaceControl.getOverlaySupport();
mSystemReady = false;
+ mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
}
public void setupSchedulerPolicies() {
@@ -694,11 +696,11 @@
synchronized (mSyncRoot) {
mSafeMode = safeMode;
mSystemReady = true;
- mIsHdrOutputControlEnabled = isDeviceConfigHdrOutputControlEnabled();
- DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_DISPLAY_MANAGER,
- BackgroundThread.getExecutor(),
+ mIsHdrOutputControlEnabled =
+ mConfigParameterProvider.isHdrOutputControlFeatureEnabled();
+ mConfigParameterProvider.addOnPropertiesChangedListener(BackgroundThread.getExecutor(),
properties -> mIsHdrOutputControlEnabled =
- isDeviceConfigHdrOutputControlEnabled());
+ mConfigParameterProvider.isHdrOutputControlFeatureEnabled());
// Just in case the top inset changed before the system was ready. At this point, any
// relevant configuration should be in place.
recordTopInsetLocked(mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY));
@@ -729,12 +731,6 @@
mContext.registerReceiver(mIdleModeReceiver, filter);
}
- private boolean isDeviceConfigHdrOutputControlEnabled() {
- return DeviceConfig.getBoolean(NAMESPACE_DISPLAY_MANAGER,
- HDR_OUTPUT_CONTROL_FLAG,
- true);
- }
-
@VisibleForTesting
Handler getDisplayHandler() {
return mHandler;
@@ -3145,8 +3141,7 @@
+ "display: " + display.getDisplayIdLocked());
return null;
}
- if (DeviceConfig.getBoolean("display_manager",
- "use_newly_structured_display_power_controller", true)) {
+ if (mConfigParameterProvider.isNewPowerControllerFeatureEnabled()) {
displayPowerController = new DisplayPowerController2(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index ffecf2b..7fc6cd0 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -2672,10 +2672,11 @@
public void setBrightness(float brightnessValue, int userSerial) {
// Update the setting, which will eventually call back into DPC to have us actually update
// the display with the new value.
+ float clampedBrightnessValue = clampScreenBrightness(brightnessValue);
mBrightnessSetting.setUserSerial(userSerial);
- mBrightnessSetting.setBrightness(brightnessValue);
+ mBrightnessSetting.setBrightness(clampedBrightnessValue);
if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
- float nits = convertToNits(brightnessValue);
+ float nits = convertToNits(clampedBrightnessValue);
if (nits >= 0) {
mBrightnessSetting.setBrightnessNitsForDefaultDisplay(nits);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 7043af8..37b9d563 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -2185,7 +2185,8 @@
@Override
public void setBrightness(float brightnessValue, int userSerial) {
- mDisplayBrightnessController.setBrightness(brightnessValue, userSerial);
+ mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightnessValue),
+ userSerial);
}
@Override
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index dec9f62..b00b7a1 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -496,7 +496,7 @@
private void loadDisplayDeviceConfig() {
// Load display device config
final Context context = getOverlayContext();
- mDisplayDeviceConfig = DisplayDeviceConfig.create(context, mPhysicalDisplayId,
+ mDisplayDeviceConfig = mInjector.createDisplayDeviceConfig(context, mPhysicalDisplayId,
mIsFirstDisplay);
// Load brightness HWC quirk
@@ -1336,6 +1336,11 @@
public SurfaceControlProxy getSurfaceControlProxy() {
return new SurfaceControlProxy();
}
+
+ public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
+ long physicalDisplayId, boolean isFirstDisplay) {
+ return DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay);
+ }
}
public interface DisplayEventListener {
diff --git a/services/core/java/com/android/server/display/NormalBrightnessModeController.java b/services/core/java/com/android/server/display/NormalBrightnessModeController.java
index dbabc24..135ebd8 100644
--- a/services/core/java/com/android/server/display/NormalBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/NormalBrightnessModeController.java
@@ -21,6 +21,7 @@
import com.android.server.display.DisplayDeviceConfig.BrightnessLimitMapType;
+import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
@@ -60,6 +61,14 @@
return recalculateMaxBrightness();
}
+ void dump(PrintWriter pw) {
+ pw.println("NormalBrightnessModeController:");
+ pw.println(" mAutoBrightnessEnabled=" + mAutoBrightnessEnabled);
+ pw.println(" mAmbientLux=" + mAmbientLux);
+ pw.println(" mMaxBrightness=" + mMaxBrightness);
+ pw.println(" mMaxBrightnessLimits=" + mMaxBrightnessLimits);
+ }
+
private boolean recalculateMaxBrightness() {
float foundAmbientBoundary = Float.MAX_VALUE;
float foundMaxBrightness = PowerManager.BRIGHTNESS_MAX;
diff --git a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
new file mode 100644
index 0000000..feebdf1
--- /dev/null
+++ b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.feature;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.display.DisplayManager;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
+import android.util.Slog;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class to access all DeviceConfig features for display_manager namespace
+ *
+ **/
+public class DeviceConfigParameterProvider {
+
+ private static final String TAG = "DisplayFeatureProvider";
+
+ private final DeviceConfigInterface mDeviceConfig;
+
+ public DeviceConfigParameterProvider(DeviceConfigInterface deviceConfig) {
+ mDeviceConfig = deviceConfig;
+ }
+
+ // feature: revamping_display_power_controller_feature
+ // parameter: use_newly_structured_display_power_controller
+ public boolean isNewPowerControllerFeatureEnabled() {
+ return mDeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_NEW_POWER_CONTROLLER, true);
+ }
+
+ // feature: hdr_output_control
+ // parameter: enable_hdr_output_control
+ public boolean isHdrOutputControlFeatureEnabled() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.HDR_OUTPUT_CONTROL_FLAG, true);
+ }
+
+ // feature: flexible_brightness_range_feature
+ // parameter: normal_brightness_mode_controller_enabled
+ public boolean isNormalBrightnessControllerFeatureEnabled() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_USE_NORMAL_BRIGHTNESS_MODE_CONTROLLER, false);
+ }
+
+ // feature: smooth_display_feature
+ // parameter: peak_refresh_rate_default
+ public float getPeakRefreshRateDefault() {
+ return mDeviceConfig.getFloat(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
+ }
+
+ // Test parameters
+ // usage e.g.: adb shell device_config put display_manager refresh_rate_in_hbm_sunlight 90
+
+ // allows to customize brightness throttling data
+ public String getBrightnessThrottlingData() {
+ return mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA, null);
+ }
+
+ public int getRefreshRateInHbmSunlight() {
+ return mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT, -1);
+ }
+
+ public int getRefreshRateInHbmHdr() {
+ return mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR, -1);
+ }
+
+
+ public int getRefreshRateInHighZone() {
+ return mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE, -1);
+ }
+
+ public int getRefreshRateInLowZone() {
+ return mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE, -1);
+ }
+
+ /** Return null if no such property or wrong format (not comma separated integers). */
+ @Nullable
+ public int[] getHighAmbientBrightnessThresholds() {
+ return getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ }
+
+ /** Return null if no such property or wrong format (not comma separated integers). */
+ @Nullable
+ public int[] getHighDisplayBrightnessThresholds() {
+ return getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ }
+
+ /** Return null if no such property or wrong format (not comma separated integers). */
+ @Nullable
+ public int[] getLowDisplayBrightnessThresholds() {
+ return getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ }
+
+ /** Return null if no such property or wrong format (not comma separated integers). */
+ @Nullable
+ public int[] getLowAmbientBrightnessThresholds() {
+ return getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ }
+
+ /** add property change listener to DeviceConfig */
+ public void addOnPropertiesChangedListener(Executor executor,
+ DeviceConfig.OnPropertiesChangedListener listener) {
+ mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ executor, listener);
+ }
+
+ /** remove property change listener from DeviceConfig */
+ public void removeOnPropertiesChangedListener(
+ DeviceConfig.OnPropertiesChangedListener listener) {
+ mDeviceConfig.removeOnPropertiesChangedListener(listener);
+ }
+
+ @Nullable
+ private int[] getIntArrayProperty(String prop) {
+ String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
+ null);
+
+ if (strArray != null) {
+ return parseIntArray(strArray);
+ }
+ return null;
+ }
+
+ @Nullable
+ private int[] parseIntArray(@NonNull String strArray) {
+ String[] items = strArray.split(",");
+ int[] array = new int[items.length];
+
+ try {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = Integer.parseInt(items[i]);
+ }
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
+ array = null;
+ }
+
+ return array;
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 1889578..11e35ce 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -20,6 +20,7 @@
import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
import static android.os.PowerManager.BRIGHTNESS_INVALID;
+import android.annotation.IntegerRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -68,6 +69,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.display.utils.SensorUtils;
@@ -84,6 +86,7 @@
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.Callable;
+import java.util.function.IntSupplier;
/**
* The DisplayModeDirector is responsible for determining what modes are allowed to be automatically
@@ -117,7 +120,7 @@
private final SensorObserver mSensorObserver;
private final HbmObserver mHbmObserver;
private final SkinThermalStatusObserver mSkinThermalStatusObserver;
- private final DeviceConfigInterface mDeviceConfig;
+ private final DeviceConfigParameterProvider mConfigParameterProvider;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
@GuardedBy("mLock")
@@ -157,7 +160,7 @@
mSupportedModesByDisplay = new SparseArray<>();
mDefaultModeByDisplay = new SparseArray<>();
mAppRequestObserver = new AppRequestObserver();
- mDeviceConfig = injector.getDeviceConfig();
+ mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig());
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
mSettingsObserver = new SettingsObserver(context, handler);
mBrightnessObserver = new BrightnessObserver(context, handler, injector);
@@ -681,9 +684,9 @@
synchronized (mLock) {
mDefaultDisplayDeviceConfig = displayDeviceConfig;
mSettingsObserver.setRefreshRates(displayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ true);
+ /* attemptReadFromFeatureParams= */ true);
mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ true);
+ /* attemptReadFromFeatureParams= */ true);
mBrightnessObserver.reloadLightSensor(displayDeviceConfig);
mHbmObserver.setupHdrRefreshRates(displayDeviceConfig);
}
@@ -1087,7 +1090,7 @@
// reading from the DeviceConfig is an intensive IO operation and having it in the
// startup phase where we thrive to keep the latency very low has significant impact.
setRefreshRates(/* displayDeviceConfig= */ null,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
}
/**
@@ -1095,8 +1098,8 @@
* if missing from DisplayDeviceConfig, and finally fallback to config.xml.
*/
public void setRefreshRates(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- setDefaultPeakRefreshRate(displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ boolean attemptReadFromFeatureParams) {
+ setDefaultPeakRefreshRate(displayDeviceConfig, attemptReadFromFeatureParams);
mDefaultRefreshRate =
(displayDeviceConfig == null) ? (float) mContext.getResources().getInteger(
R.integer.config_defaultRefreshRate)
@@ -1113,9 +1116,9 @@
cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
this);
- Float deviceConfigDefaultPeakRefresh =
- mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
- if (deviceConfigDefaultPeakRefresh != null) {
+ float deviceConfigDefaultPeakRefresh =
+ mConfigParameterProvider.getPeakRefreshRateDefault();
+ if (deviceConfigDefaultPeakRefresh != -1) {
mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh;
}
@@ -1137,7 +1140,7 @@
synchronized (mLock) {
if (defaultPeakRefreshRate == null) {
setDefaultPeakRefreshRate(mDefaultDisplayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
updateRefreshRateSettingLocked();
} else if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) {
mDefaultPeakRefreshRate = defaultPeakRefreshRate;
@@ -1171,18 +1174,17 @@
}
private void setDefaultPeakRefreshRate(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- Float defaultPeakRefreshRate = null;
+ boolean attemptReadFromFeatureParams) {
+ float defaultPeakRefreshRate = -1;
- if (attemptLoadingFromDeviceConfig) {
+ if (attemptReadFromFeatureParams) {
try {
- defaultPeakRefreshRate =
- mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
+ defaultPeakRefreshRate = mConfigParameterProvider.getPeakRefreshRateDefault();
} catch (Exception exception) {
// Do nothing
}
}
- if (defaultPeakRefreshRate == null) {
+ if (defaultPeakRefreshRate == -1) {
defaultPeakRefreshRate =
(displayDeviceConfig == null) ? (float) mContext.getResources().getInteger(
R.integer.config_defaultPeakRefreshRate)
@@ -1528,7 +1530,7 @@
mHandler = handler;
mInjector = injector;
updateBlockingZoneThresholds(/* displayDeviceConfig= */ null,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
mRefreshRateInHighZone = context.getResources().getInteger(
R.integer.config_fixedRefreshRateInHighZone);
}
@@ -1537,10 +1539,10 @@
* This is used to update the blocking zone thresholds from the DeviceConfig, which
* if missing from DisplayDeviceConfig, and finally fallback to config.xml.
*/
- public void updateBlockingZoneThresholds(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- loadLowBrightnessThresholds(displayDeviceConfig, attemptLoadingFromDeviceConfig);
- loadHighBrightnessThresholds(displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ public void updateBlockingZoneThresholds(@Nullable DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptReadFromFeatureParams) {
+ loadLowBrightnessThresholds(displayDeviceConfig, attemptReadFromFeatureParams);
+ loadHighBrightnessThresholds(displayDeviceConfig, attemptReadFromFeatureParams);
}
@VisibleForTesting
@@ -1579,20 +1581,20 @@
return mRefreshRateInLowZone;
}
- private void loadLowBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- loadRefreshRateInHighZone(displayDeviceConfig, attemptLoadingFromDeviceConfig);
- loadRefreshRateInLowZone(displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ private void loadLowBrightnessThresholds(@Nullable DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptReadFromFeatureParams) {
+ loadRefreshRateInHighZone(displayDeviceConfig, attemptReadFromFeatureParams);
+ loadRefreshRateInLowZone(displayDeviceConfig, attemptReadFromFeatureParams);
mLowDisplayBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getLowDisplayBrightnessThresholds(),
R.array.config_brightnessThresholdsOfPeakRefreshRate,
- displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ displayDeviceConfig, attemptReadFromFeatureParams);
mLowAmbientBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getLowAmbientBrightnessThresholds(),
R.array.config_ambientThresholdsOfPeakRefreshRate,
- displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ displayDeviceConfig, attemptReadFromFeatureParams);
if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
throw new RuntimeException("display low brightness threshold array and ambient "
+ "brightness threshold array have different length: "
@@ -1604,55 +1606,55 @@
}
private void loadRefreshRateInLowZone(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- int refreshRateInLowZone =
- (displayDeviceConfig == null) ? mContext.getResources().getInteger(
- R.integer.config_defaultRefreshRateInZone)
- : displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate();
- if (attemptLoadingFromDeviceConfig) {
+ boolean attemptReadFromFeatureParams) {
+ int refreshRateInLowZone = -1;
+ if (attemptReadFromFeatureParams) {
try {
- refreshRateInLowZone = mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
- refreshRateInLowZone);
+ refreshRateInLowZone = mConfigParameterProvider.getRefreshRateInLowZone();
} catch (Exception exception) {
// Do nothing
}
}
+ if (refreshRateInLowZone == -1) {
+ refreshRateInLowZone = (displayDeviceConfig == null)
+ ? mContext.getResources().getInteger(
+ R.integer.config_defaultRefreshRateInZone)
+ : displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate();
+ }
mRefreshRateInLowZone = refreshRateInLowZone;
}
private void loadRefreshRateInHighZone(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- int refreshRateInHighZone =
- (displayDeviceConfig == null) ? mContext.getResources().getInteger(
- R.integer.config_fixedRefreshRateInHighZone) : displayDeviceConfig
- .getDefaultHighBlockingZoneRefreshRate();
- if (attemptLoadingFromDeviceConfig) {
+ boolean attemptReadFromFeatureParams) {
+ int refreshRateInHighZone = -1;
+ if (attemptReadFromFeatureParams) {
try {
- refreshRateInHighZone = mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
- refreshRateInHighZone);
+ refreshRateInHighZone = mConfigParameterProvider.getRefreshRateInHighZone();
} catch (Exception exception) {
// Do nothing
}
}
+ if (refreshRateInHighZone == -1) {
+ refreshRateInHighZone = (displayDeviceConfig == null)
+ ? mContext.getResources().getInteger(
+ R.integer.config_fixedRefreshRateInHighZone)
+ : displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate();
+ }
mRefreshRateInHighZone = refreshRateInHighZone;
}
private void loadHighBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
+ boolean attemptReadFromFeatureParams) {
mHighDisplayBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds(),
+ () -> mConfigParameterProvider.getHighDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getHighDisplayBrightnessThresholds(),
R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ displayDeviceConfig, attemptReadFromFeatureParams);
mHighAmbientBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds(),
+ () -> mConfigParameterProvider.getHighAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getHighAmbientBrightnessThresholds(),
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ displayDeviceConfig, attemptReadFromFeatureParams);
if (mHighDisplayBrightnessThresholds.length
!= mHighAmbientBrightnessThresholds.length) {
throw new RuntimeException("display high brightness threshold array and ambient "
@@ -1668,10 +1670,10 @@
Callable<int[]> loadFromDeviceConfigDisplaySettingsCallable,
Callable<int[]> loadFromDisplayDeviceConfigCallable,
int brightnessThresholdOfFixedRefreshRateKey,
- DisplayDeviceConfig displayDeviceConfig, boolean attemptLoadingFromDeviceConfig) {
+ DisplayDeviceConfig displayDeviceConfig, boolean attemptReadFromFeatureParams) {
int[] brightnessThresholds = null;
- if (attemptLoadingFromDeviceConfig) {
+ if (attemptReadFromFeatureParams) {
try {
brightnessThresholds =
loadFromDeviceConfigDisplaySettingsCallable.call();
@@ -1715,9 +1717,9 @@
// DeviceConfig is accessible after system ready.
int[] lowDisplayBrightnessThresholds =
- mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds();
+ mConfigParameterProvider.getLowDisplayBrightnessThresholds();
int[] lowAmbientBrightnessThresholds =
- mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds();
+ mConfigParameterProvider.getLowAmbientBrightnessThresholds();
if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null
&& lowDisplayBrightnessThresholds.length
@@ -1727,9 +1729,9 @@
}
int[] highDisplayBrightnessThresholds =
- mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds();
+ mConfigParameterProvider.getHighDisplayBrightnessThresholds();
int[] highAmbientBrightnessThresholds =
- mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds();
+ mConfigParameterProvider.getHighAmbientBrightnessThresholds();
if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null
&& highDisplayBrightnessThresholds.length
@@ -1738,14 +1740,12 @@
mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds;
}
- final int refreshRateInLowZone = mDeviceConfigDisplaySettings
- .getRefreshRateInLowZone();
+ final int refreshRateInLowZone = mConfigParameterProvider.getRefreshRateInLowZone();
if (refreshRateInLowZone != -1) {
mRefreshRateInLowZone = refreshRateInLowZone;
}
- final int refreshRateInHighZone = mDeviceConfigDisplaySettings
- .getRefreshRateInHighZone();
+ final int refreshRateInHighZone = mConfigParameterProvider.getRefreshRateInHighZone();
if (refreshRateInHighZone != -1) {
mRefreshRateInHighZone = refreshRateInHighZone;
}
@@ -1799,15 +1799,15 @@
displayDeviceConfig = mDefaultDisplayDeviceConfig;
}
mLowDisplayBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getLowDisplayBrightnessThresholds(),
R.array.config_brightnessThresholdsOfPeakRefreshRate,
- displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
mLowAmbientBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getLowAmbientBrightnessThresholds(),
R.array.config_ambientThresholdsOfPeakRefreshRate,
- displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
}
restartObserver();
}
@@ -1822,7 +1822,7 @@
// from there.
synchronized (mLock) {
loadRefreshRateInLowZone(mDefaultDisplayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
}
restartObserver();
} else if (refreshRate != mRefreshRateInLowZone) {
@@ -1843,15 +1843,15 @@
displayDeviceConfig = mDefaultDisplayDeviceConfig;
}
mHighDisplayBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getHighDisplayBrightnessThresholds(),
R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
mHighAmbientBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds(),
+ () -> mConfigParameterProvider.getHighAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getHighAmbientBrightnessThresholds(),
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
}
restartObserver();
}
@@ -1866,7 +1866,7 @@
// from there.
synchronized (mLock) {
loadRefreshRateInHighZone(mDefaultDisplayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
}
restartObserver();
} else if (refreshRate != mRefreshRateInHighZone) {
@@ -2675,113 +2675,55 @@
private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
public void startListening() {
- mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ mConfigParameterProvider.addOnPropertiesChangedListener(
BackgroundThread.getExecutor(), this);
}
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getLowDisplayBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ private int getRefreshRateInHbmHdr(DisplayDeviceConfig displayDeviceConfig) {
+ return getRefreshRate(
+ () -> mConfigParameterProvider.getRefreshRateInHbmHdr(),
+ () -> displayDeviceConfig.getDefaultRefreshRateInHbmHdr(),
+ R.integer.config_defaultRefreshRateInHbmHdr,
+ displayDeviceConfig
+ );
}
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getLowAmbientBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ private int getRefreshRateInHbmSunlight(DisplayDeviceConfig displayDeviceConfig) {
+ return getRefreshRate(
+ () -> mConfigParameterProvider.getRefreshRateInHbmSunlight(),
+ () -> displayDeviceConfig.getDefaultRefreshRateInHbmSunlight(),
+ R.integer.config_defaultRefreshRateInHbmSunlight,
+ displayDeviceConfig
+ );
}
- public int getRefreshRateInLowZone() {
- return mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE, -1);
-
- }
-
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getHighDisplayBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
- }
-
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getHighAmbientBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
- }
-
- public int getRefreshRateInHighZone() {
- return mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
- -1);
- }
-
- public int getRefreshRateInHbmHdr(DisplayDeviceConfig displayDeviceConfig) {
- int refreshRate =
- (displayDeviceConfig == null) ? mContext.getResources().getInteger(
- R.integer.config_defaultRefreshRateInHbmHdr)
- : displayDeviceConfig.getDefaultRefreshRateInHbmHdr();
+ private int getRefreshRate(IntSupplier fromConfigPram, IntSupplier fromDisplayDeviceConfig,
+ @IntegerRes int configKey, DisplayDeviceConfig displayDeviceConfig) {
+ int refreshRate = -1;
try {
- refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR,
- refreshRate);
- } catch (NullPointerException e) {
+ refreshRate = fromConfigPram.getAsInt();
+ } catch (NullPointerException npe) {
// Do Nothing
}
- return refreshRate;
- }
-
- public int getRefreshRateInHbmSunlight(DisplayDeviceConfig displayDeviceConfig) {
- int refreshRate =
- (displayDeviceConfig == null) ? mContext.getResources()
- .getInteger(R.integer.config_defaultRefreshRateInHbmSunlight)
- : displayDeviceConfig.getDefaultRefreshRateInHbmSunlight();
- try {
- refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT,
- refreshRate);
- } catch (NullPointerException e) {
- // Do Nothing
+ if (refreshRate == -1) {
+ refreshRate = (displayDeviceConfig == null)
+ ? mContext.getResources().getInteger(configKey)
+ : fromDisplayDeviceConfig.getAsInt();
}
return refreshRate;
}
- /*
- * Return null if no such property
- */
- public Float getDefaultPeakRefreshRate() {
- float defaultPeakRefreshRate = mDeviceConfig.getFloat(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
-
- if (defaultPeakRefreshRate == -1) {
- return null;
- }
- return defaultPeakRefreshRate;
- }
-
@Override
public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
- Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
+ float defaultPeakRefreshRate = mConfigParameterProvider.getPeakRefreshRateDefault();
mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
- defaultPeakRefreshRate).sendToTarget();
+ defaultPeakRefreshRate == -1 ? null : defaultPeakRefreshRate).sendToTarget();
- int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds();
- int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds();
- final int refreshRateInLowZone = getRefreshRateInLowZone();
+ int[] lowDisplayBrightnessThresholds =
+ mConfigParameterProvider.getLowDisplayBrightnessThresholds();
+ int[] lowAmbientBrightnessThresholds =
+ mConfigParameterProvider.getLowAmbientBrightnessThresholds();
+ final int refreshRateInLowZone = mConfigParameterProvider.getRefreshRateInLowZone();
mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
@@ -2790,9 +2732,11 @@
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone,
0).sendToTarget();
- int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
- int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds();
- final int refreshRateInHighZone = getRefreshRateInHighZone();
+ int[] highDisplayBrightnessThresholds =
+ mConfigParameterProvider.getHighDisplayBrightnessThresholds();
+ int[] highAmbientBrightnessThresholds =
+ mConfigParameterProvider.getHighAmbientBrightnessThresholds();
+ final int refreshRateInHighZone = mConfigParameterProvider.getRefreshRateInHighZone();
mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
@@ -2814,33 +2758,6 @@
.sendToTarget();
}
}
-
- private int[] getIntArrayProperty(String prop) {
- String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
- null);
-
- if (strArray != null) {
- return parseIntArray(strArray);
- }
-
- return null;
- }
-
- private int[] parseIntArray(@NonNull String strArray) {
- String[] items = strArray.split(",");
- int[] array = new int[items.length];
-
- try {
- for (int i = 0; i < array.length; i++) {
- array[i] = Integer.parseInt(items[i]);
- }
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
- array = null;
- }
-
- return array;
- }
}
interface Injector {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 7d0d5a7..b5e8195 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -16,6 +16,8 @@
package com.android.server.hdmi;
+import static com.android.server.hdmi.HdmiControlService.DEVICE_CLEANUP_TIMEOUT;
+
import android.annotation.CallSuper;
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
@@ -61,9 +63,6 @@
private static final int MAX_HDMI_ACTIVE_SOURCE_HISTORY = 10;
private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1;
private static final int MSG_USER_CONTROL_RELEASE_TIMEOUT = 2;
- // Timeout in millisecond for device clean up (5s).
- // Normal actions timeout is 2s but some of them would have several sequence of timeout.
- private static final int DEVICE_CLEANUP_TIMEOUT = 5000;
// Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior.
// When it expires, we can assume <User Control Release> is received.
private static final int FOLLOWER_SAFETY_TIMEOUT = 550;
@@ -175,6 +174,14 @@
};
/**
+ * A callback interface used by local devices use to indicate that they have finished their part
+ * of the standby process.
+ */
+ interface StandbyCompletedCallback {
+ void onStandbyCompleted();
+ }
+
+ /**
* A callback interface to get notified when all pending action is cleared. It can be called
* when timeout happened.
*/
@@ -1260,8 +1267,14 @@
* messages like <Standby>
* @param standbyAction Intent action that drives the standby process, either {@link
* HdmiControlService#STANDBY_SCREEN_OFF} or {@link HdmiControlService#STANDBY_SHUTDOWN}
+ * @param callback callback invoked after the standby process for the local device is completed.
*/
- protected void onStandby(boolean initiatedByCec, int standbyAction) {}
+ protected void onStandby(boolean initiatedByCec, int standbyAction,
+ StandbyCompletedCallback callback) {}
+
+ protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ onStandby(initiatedByCec, standbyAction, null);
+ }
/**
* Called when the initialization of local devices is complete.
@@ -1422,6 +1435,16 @@
}
}
+ @ServiceThreadOnly
+ @VisibleForTesting
+ public void invokeStandbyCompletedCallback(StandbyCompletedCallback callback) {
+ assertRunOnServiceThread();
+ if (callback == null) {
+ return;
+ }
+ callback.onStandbyCompleted();
+ }
+
void sendUserControlPressedAndReleased(int targetAddress, int cecKeycode) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildUserControlPressed(
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 184bdd7..b3aa351 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -21,6 +21,7 @@
import static com.android.server.hdmi.Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
import static com.android.server.hdmi.Constants.PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
import static com.android.server.hdmi.Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
+import static com.android.server.hdmi.HdmiControlService.SendMessageCallback;
import android.annotation.Nullable;
import android.content.ActivityNotFoundException;
@@ -243,7 +244,8 @@
@Override
@ServiceThreadOnly
- protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ protected void onStandby(boolean initiatedByCec, int standbyAction,
+ StandbyCompletedCallback callback) {
assertRunOnServiceThread();
// Invalidate the internal active source record when goes to standby
// This set will also update mIsActiveSource
@@ -256,7 +258,7 @@
Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL,
isSystemAudioActivated() ? "true" : "false");
}
- terminateSystemAudioMode();
+ terminateSystemAudioMode(callback);
}
@Override
@@ -1092,9 +1094,16 @@
}
protected void terminateSystemAudioMode() {
+ terminateSystemAudioMode(null);
+ }
+
+ // Since this method is not just called during the standby process, the callback should be
+ // generalized in the future.
+ protected void terminateSystemAudioMode(StandbyCompletedCallback callback) {
// remove pending initiation actions
removeAction(SystemAudioInitiationActionFromAvr.class);
if (!isSystemAudioActivated()) {
+ invokeStandbyCompletedCallback(callback);
return;
}
@@ -1102,7 +1111,13 @@
// send <Set System Audio Mode> [“Off”]
mService.sendCecCommand(
HdmiCecMessageBuilder.buildSetSystemAudioMode(
- getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST, false));
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST, false),
+ new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ invokeStandbyCompletedCallback(callback);
+ }
+ });
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index c73a07d..dc416b2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -28,7 +28,6 @@
import android.os.Binder;
import android.os.Handler;
import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
import android.os.SystemProperties;
import android.sysprop.HdmiProperties;
import android.util.Slog;
@@ -238,9 +237,11 @@
@Override
@ServiceThreadOnly
- protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ protected void onStandby(boolean initiatedByCec, int standbyAction,
+ StandbyCompletedCallback callback) {
assertRunOnServiceThread();
if (!mService.isCecControlEnabled()) {
+ invokeStandbyCompletedCallback(callback);
return;
}
boolean wasActiveSource = isActiveSource();
@@ -248,12 +249,20 @@
mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
"HdmiCecLocalDevicePlayback#onStandby()");
if (!wasActiveSource) {
+ invokeStandbyCompletedCallback(callback);
return;
}
+ SendMessageCallback sendMessageCallback = new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ invokeStandbyCompletedCallback(callback);
+ }
+ };
if (initiatedByCec) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildInactiveSource(
- getDeviceInfo().getLogicalAddress(), mService.getPhysicalAddress()));
+ getDeviceInfo().getLogicalAddress(), mService.getPhysicalAddress()),
+ sendMessageCallback);
return;
}
switch (standbyAction) {
@@ -266,7 +275,8 @@
case HdmiControlManager.POWER_CONTROL_MODE_TV:
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(
- getDeviceInfo().getLogicalAddress(), Constants.ADDR_TV));
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_TV),
+ sendMessageCallback);
break;
case HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM:
mService.sendCecCommand(
@@ -275,19 +285,19 @@
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(
getDeviceInfo().getLogicalAddress(),
- Constants.ADDR_AUDIO_SYSTEM));
+ Constants.ADDR_AUDIO_SYSTEM), sendMessageCallback);
break;
case HdmiControlManager.POWER_CONTROL_MODE_BROADCAST:
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(
getDeviceInfo().getLogicalAddress(),
- Constants.ADDR_BROADCAST));
+ Constants.ADDR_BROADCAST), sendMessageCallback);
break;
case HdmiControlManager.POWER_CONTROL_MODE_NONE:
mService.sendCecCommand(
HdmiCecMessageBuilder.buildInactiveSource(
getDeviceInfo().getLogicalAddress(),
- mService.getPhysicalAddress()));
+ mService.getPhysicalAddress()), sendMessageCallback);
break;
}
break;
@@ -295,7 +305,8 @@
// ACTION_SHUTDOWN is taken as a signal to power off all the devices.
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(
- getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST));
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST),
+ sendMessageCallback);
break;
}
}
@@ -590,7 +601,7 @@
}
private class SystemWakeLock implements ActiveWakeLock {
- private final WakeLock mWakeLock;
+ private final WakeLockWrapper mWakeLock;
public SystemWakeLock() {
mWakeLock = mService.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.setReferenceCounted(false);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 7087062..6cca1bd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -377,6 +377,7 @@
return;
}
int oldPath = getActivePortId() != Constants.INVALID_PORT_ID
+ && getActivePortId() != Constants.CEC_SWITCH_HOME
? mService.portIdToPath(getActivePortId()) : getDeviceInfo().getPhysicalAddress();
setActivePath(oldPath);
if (mSkipRoutingControl) {
@@ -1399,10 +1400,12 @@
@Override
@ServiceThreadOnly
- protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ protected void onStandby(boolean initiatedByCec, int standbyAction,
+ StandbyCompletedCallback callback) {
assertRunOnServiceThread();
// Seq #11
if (!mService.isCecControlEnabled()) {
+ invokeStandbyCompletedCallback(callback);
return;
}
boolean sendStandbyOnSleep =
@@ -1412,7 +1415,15 @@
if (!initiatedByCec && sendStandbyOnSleep) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(
- getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST));
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST),
+ new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ invokeStandbyCompletedCallback(callback);
+ }
+ });
+ } else {
+ invokeStandbyCompletedCallback(callback);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 2249bd4..91cf503 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -21,8 +21,10 @@
import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_DISABLED;
import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_ENABLED;
import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
+import static android.hardware.hdmi.HdmiControlManager.POWER_CONTROL_MODE_NONE;
import static android.hardware.hdmi.HdmiControlManager.SOUNDBAR_MODE_DISABLED;
import static android.hardware.hdmi.HdmiControlManager.SOUNDBAR_MODE_ENABLED;
+import static android.hardware.hdmi.HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
import static com.android.server.hdmi.Constants.DISABLED;
@@ -110,6 +112,7 @@
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
+import com.android.server.hdmi.HdmiCecLocalDevice.StandbyCompletedCallback;
import libcore.util.EmptyArray;
@@ -224,6 +227,10 @@
public @interface WakeReason {
}
+ // Timeout in millisecond for device clean up (5s).
+ // Normal actions timeout is 2s but some of them would have several sequences of timeout.
+ static final int DEVICE_CLEANUP_TIMEOUT = 5000;
+
@VisibleForTesting
static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_HDMI = new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_HDMI, "");
@@ -492,6 +499,9 @@
private DeviceConfigWrapper mDeviceConfig;
@Nullable
+ private WakeLockWrapper mWakeLock;
+
+ @Nullable
private PowerManagerWrapper mPowerManager;
@Nullable
@@ -3040,7 +3050,7 @@
}
String powerControlMode = getHdmiCecConfig().getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
- if (powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_NONE)) {
+ if (powerControlMode.equals(POWER_CONTROL_MODE_NONE)) {
return false;
}
int hdmiCecEnabled = getHdmiCecConfig().getIntValue(
@@ -3687,6 +3697,9 @@
@ServiceThreadOnly
@VisibleForTesting
protected void onStandby(final int standbyAction) {
+ if (shouldAcquireWakeLockOnStandby()) {
+ acquireWakeLock();
+ }
mWakeUpMessageReceived = false;
assertRunOnServiceThread();
mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY,
@@ -3752,7 +3765,8 @@
return mMenuLanguage;
}
- private void disableCecLocalDevices(PendingActionClearedCallback callback) {
+ @VisibleForTesting
+ protected void disableCecLocalDevices(PendingActionClearedCallback callback) {
if (mCecController != null) {
for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
device.disableDevice(mStandbyMessageReceived, callback);
@@ -3780,25 +3794,81 @@
* cleared during standby. In this case, it does not execute the standby flow.
*/
@ServiceThreadOnly
- private void onPendingActionsCleared(int standbyAction) {
+ @VisibleForTesting
+ protected void onPendingActionsCleared(int standbyAction) {
assertRunOnServiceThread();
Slog.v(TAG, "onPendingActionsCleared");
+ int localDevicesCount = getAllCecLocalDevices().size();
+ final int[] countStandbyCompletedDevices = new int[1];
+ StandbyCompletedCallback callback = new StandbyCompletedCallback() {
+ @Override
+ public void onStandbyCompleted() {
+ if (localDevicesCount < ++countStandbyCompletedDevices[0]) {
+ return;
+ }
+
+ releaseWakeLock();
+ if (isAudioSystemDevice() || !isPowerStandby()) {
+ return;
+ }
+ mCecController.enableSystemCecControl(false);
+ mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
+ }
+ };
if (mPowerStatusController.isPowerStatusTransientToStandby()) {
mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_STANDBY);
for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
- device.onStandby(mStandbyMessageReceived, standbyAction);
- }
- if (!isAudioSystemDevice()) {
- mCecController.enableSystemCecControl(false);
- mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
+ device.onStandby(mStandbyMessageReceived, standbyAction, callback);
}
}
-
// Always reset this flag to set up for the next standby
mStandbyMessageReceived = false;
}
+ private boolean shouldAcquireWakeLockOnStandby() {
+ boolean sendStandbyOnSleep = false;
+ if (tv() != null) {
+ sendStandbyOnSleep = mHdmiCecConfig.getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
+ == TV_SEND_STANDBY_ON_SLEEP_ENABLED;
+ } else if (playback() != null) {
+ sendStandbyOnSleep = !mHdmiCecConfig.getStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)
+ .equals(POWER_CONTROL_MODE_NONE);
+ }
+
+ return isCecControlEnabled() && isPowerOnOrTransient() && sendStandbyOnSleep;
+ }
+
+ /**
+ * Acquire the wake lock used to hold the system awake until the standby process is finished.
+ */
+ @VisibleForTesting
+ protected void acquireWakeLock() {
+ releaseWakeLock();
+ mWakeLock = mPowerManager.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mWakeLock.acquire(DEVICE_CLEANUP_TIMEOUT);
+ }
+
+ /**
+ * Release the wake lock acquired when the standby process started.
+ */
+ @VisibleForTesting
+ protected void releaseWakeLock() {
+ if (mWakeLock != null) {
+ try {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Exception when releasing wake lock.");
+ }
+ mWakeLock = null;
+ }
+ }
+
@VisibleForTesting
void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, vendorId);
diff --git a/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java b/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java
index f081068..7530b3b 100644
--- a/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java
+++ b/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
/**
* Abstraction around {@link PowerManager} to allow faking PowerManager in tests.
@@ -44,7 +43,54 @@
mPowerManager.goToSleep(time, reason, flags);
}
- WakeLock newWakeLock(int levelAndFlags, String tag) {
- return mPowerManager.newWakeLock(levelAndFlags, tag);
+ WakeLockWrapper newWakeLock(int levelAndFlags, String tag) {
+ return new DefaultWakeLockWrapper(mPowerManager.newWakeLock(levelAndFlags, tag));
+ }
+
+ /**
+ * "Default" wrapper for {@link PowerManager.WakeLock}, as opposed to a "Fake" wrapper for
+ * testing - see {@link FakePowerManagerWrapper.FakeWakeLockWrapper}.
+ *
+ * Stores an instance of {@link PowerManager.WakeLock} and directly passes method calls to that
+ * instance.
+ */
+ public static class DefaultWakeLockWrapper implements WakeLockWrapper {
+
+ private static final String TAG = "DefaultWakeLockWrapper";
+
+ private final PowerManager.WakeLock mWakeLock;
+
+ private DefaultWakeLockWrapper(PowerManager.WakeLock wakeLock) {
+ mWakeLock = wakeLock;
+ }
+
+ @Override
+ public void acquire(long timeout) {
+ mWakeLock.acquire(timeout);
+ }
+
+ @Override
+ public void acquire() {
+ mWakeLock.acquire();
+ }
+
+ /**
+ * @throws RuntimeException WakeLock can throw this exception if it is not released
+ * successfully.
+ */
+ @Override
+ public void release() throws RuntimeException {
+ mWakeLock.release();
+ }
+
+ @Override
+ public boolean isHeld() {
+ return mWakeLock.isHeld();
+ }
+
+ @Override
+ public void setReferenceCounted(boolean value) {
+ mWakeLock.setReferenceCounted(value);
+ }
}
}
diff --git a/services/core/java/com/android/server/hdmi/WakeLockWrapper.java b/services/core/java/com/android/server/hdmi/WakeLockWrapper.java
new file mode 100644
index 0000000..f689910
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/WakeLockWrapper.java
@@ -0,0 +1,52 @@
+/*
+ * 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.hdmi;
+
+/**
+ * Interface with the methods from {@link PowerManager.WakeLock} used by the HDMI control framework.
+ * Allows the class to be faked for the tests.
+ *
+ * See implementations {@link DefaultWakeLockWrapper} and
+ * {@link FakePowerManagerWrapper.FakeWakeLockWrapper}.
+ */
+public interface WakeLockWrapper {
+
+ /**
+ * Wraps {@link PowerManager.WakeLock#acquire(long)};
+ */
+ void acquire(long timeout);
+
+ /**
+ * Wraps {@link PowerManager.WakeLock#acquire()};
+ */
+ void acquire();
+
+ /**
+ * Wraps {@link PowerManager.WakeLock#release()};
+ */
+ void release();
+
+ /**
+ * Wraps {@link PowerManager.WakeLock#isHeld()};
+ */
+ boolean isHeld();
+
+ /**
+ * Wraps {@link PowerManager.WakeLock#setReferenceCounted(boolean)};
+ */
+ void setReferenceCounted(boolean value);
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index ce482a9..e78adda 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2793,6 +2793,11 @@
void notifyConfigurationChanged();
/**
+ * This callback is invoked when the pointer location changes.
+ */
+ void notifyPointerLocationChanged(boolean pointerLocationEnabled);
+
+ /**
* This callback is invoked when the camera lens cover switch changes state.
* @param whenNanos the time when the change occurred
* @param lensCovered true is the lens is covered
@@ -3374,6 +3379,10 @@
}
}
+ void updatePointerLocationEnabled(boolean enabled) {
+ mWindowManagerCallbacks.notifyPointerLocationChanged(enabled);
+ }
+
void updateFocusEventDebugViewEnabled(boolean enabled) {
FocusEventDebugView view;
synchronized (mFocusEventDebugViewLock) {
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index 3716e1f..cf7c692 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -70,6 +70,8 @@
(reason) -> updateTouchpadRightClickZoneEnabled()),
Map.entry(Settings.System.getUriFor(Settings.System.SHOW_TOUCHES),
(reason) -> updateShowTouches()),
+ Map.entry(Settings.System.getUriFor(Settings.System.POINTER_LOCATION),
+ (reason) -> updatePointerLocation()),
Map.entry(
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON),
(reason) -> updateAccessibilityLargePointer()),
@@ -156,6 +158,11 @@
mNative.setShowTouches(getBoolean(Settings.System.SHOW_TOUCHES, false));
}
+ private void updatePointerLocation() {
+ mService.updatePointerLocationEnabled(
+ getBoolean(Settings.System.POINTER_LOCATION, false));
+ }
+
private void updateShowKeyPresses() {
mService.updateFocusEventDebugViewEnabled(
getBoolean(Settings.System.SHOW_KEY_PRESSES, false));
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 6ba7585..d700c6a 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -36,6 +36,7 @@
import android.hardware.weaver.WeaverConfig;
import android.hardware.weaver.WeaverReadResponse;
import android.hardware.weaver.WeaverReadStatus;
+import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -505,7 +506,7 @@
private final Context mContext;
private LockSettingsStorage mStorage;
- private IWeaver mWeaver;
+ private volatile IWeaver mWeaver;
private WeaverConfig mWeaverConfig;
private PasswordSlotManager mPasswordSlotManager;
@@ -536,13 +537,33 @@
}
}
- private IWeaver getWeaverService() {
+ private class WeaverDiedRecipient implements IBinder.DeathRecipient {
+ // Not synchronized on the outer class, since setting the pointer to null is atomic, and we
+ // don't want to have to worry about any sort of deadlock here.
+ @Override
+ public void binderDied() {
+ // Weaver died. Try to recover by setting mWeaver to null, which makes
+ // getWeaverService() look up the service again. This is done only as a simple
+ // robustness measure; it should not be relied on. If this triggers, the root cause is
+ // almost certainly a bug in the device's Weaver implementation, which must be fixed.
+ Slog.wtf(TAG, "Weaver service has died");
+ mWeaver.asBinder().unlinkToDeath(this, 0);
+ mWeaver = null;
+ }
+ }
+
+ private @Nullable IWeaver getWeaverServiceInternal() {
// Try to get the AIDL service first
try {
IWeaver aidlWeaver = IWeaver.Stub.asInterface(
ServiceManager.waitForDeclaredService(IWeaver.DESCRIPTOR + "/default"));
if (aidlWeaver != null) {
Slog.i(TAG, "Using AIDL weaver service");
+ try {
+ aidlWeaver.asBinder().linkToDeath(new WeaverDiedRecipient(), 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to register Weaver death recipient", e);
+ }
return aidlWeaver;
}
} catch (SecurityException e) {
@@ -568,15 +589,20 @@
return LockPatternUtils.isAutoPinConfirmFeatureAvailable();
}
- private synchronized boolean isWeaverAvailable() {
- if (mWeaver != null) {
- return true;
+ /**
+ * Returns a handle to the Weaver service, or null if Weaver is unavailable. Note that not all
+ * devices support Weaver.
+ */
+ private synchronized @Nullable IWeaver getWeaverService() {
+ IWeaver weaver = mWeaver;
+ if (weaver != null) {
+ return weaver;
}
// Re-initialize weaver in case there was a transient error preventing access to it.
- IWeaver weaver = getWeaverService();
+ weaver = getWeaverServiceInternal();
if (weaver == null) {
- return false;
+ return null;
}
final WeaverConfig weaverConfig;
@@ -584,19 +610,18 @@
weaverConfig = weaver.getConfig();
} catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Failed to get weaver config", e);
- return false;
+ return null;
}
if (weaverConfig == null || weaverConfig.slots <= 0) {
Slog.e(TAG, "Invalid weaver config");
- return false;
+ return null;
}
mWeaver = weaver;
mWeaverConfig = weaverConfig;
mPasswordSlotManager.refreshActiveSlots(getUsedWeaverSlots());
Slog.i(TAG, "Weaver service initialized");
-
- return true;
+ return weaver;
}
/**
@@ -606,7 +631,7 @@
*
* @return the value stored in the weaver slot, or null if the operation fails
*/
- private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value) {
+ private byte[] weaverEnroll(IWeaver weaver, int slot, byte[] key, @Nullable byte[] value) {
if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
throw new IllegalArgumentException("Invalid slot for weaver");
}
@@ -619,7 +644,7 @@
value = SecureRandomUtils.randomBytes(mWeaverConfig.valueSize);
}
try {
- mWeaver.write(slot, key, value);
+ weaver.write(slot, key, value);
} catch (RemoteException e) {
Slog.e(TAG, "weaver write binder call failed, slot: " + slot, e);
return null;
@@ -648,7 +673,7 @@
* the verification is successful, throttled or failed. If successful, the bound secret
* is also returned.
*/
- private VerifyCredentialResponse weaverVerify(int slot, byte[] key) {
+ private VerifyCredentialResponse weaverVerify(IWeaver weaver, int slot, byte[] key) {
if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
throw new IllegalArgumentException("Invalid slot for weaver");
}
@@ -659,7 +684,7 @@
}
final WeaverReadResponse readResponse;
try {
- readResponse = mWeaver.read(slot, key);
+ readResponse = weaver.read(slot, key);
} catch (RemoteException e) {
Slog.e(TAG, "weaver read failed, slot: " + slot, e);
return VerifyCredentialResponse.ERROR;
@@ -870,14 +895,15 @@
int slot = loadWeaverSlot(protectorId, userId);
destroyState(WEAVER_SLOT_NAME, protectorId, userId);
if (slot != INVALID_WEAVER_SLOT) {
- if (!isWeaverAvailable()) {
+ final IWeaver weaver = getWeaverService();
+ if (weaver == null) {
Slog.e(TAG, "Cannot erase Weaver slot because Weaver is unavailable");
return;
}
Set<Integer> usedSlots = getUsedWeaverSlots();
if (!usedSlots.contains(slot)) {
Slogf.i(TAG, "Erasing Weaver slot %d", slot);
- weaverEnroll(slot, null, null);
+ weaverEnroll(weaver, slot, null, null);
mPasswordSlotManager.markSlotDeleted(slot);
} else {
Slogf.i(TAG, "Weaver slot %d was already reused; not erasing it", slot);
@@ -955,13 +981,14 @@
Slogf.i(TAG, "Creating LSKF-based protector %016x for user %d", protectorId, userId);
- if (isWeaverAvailable()) {
+ final IWeaver weaver = getWeaverService();
+ if (weaver != null) {
// Weaver is available, so make the protector use it to verify the LSKF. Do this even
// if the LSKF is empty, as that gives us support for securely deleting the protector.
int weaverSlot = getNextAvailableWeaverSlot();
Slogf.i(TAG, "Enrolling LSKF for user %d into Weaver slot %d", userId, weaverSlot);
- byte[] weaverSecret = weaverEnroll(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf),
- null);
+ byte[] weaverSecret = weaverEnroll(weaver, weaverSlot,
+ stretchedLskfToWeaverKey(stretchedLskf), null);
if (weaverSecret == null) {
throw new IllegalStateException(
"Fail to enroll user password under weaver " + userId);
@@ -1048,7 +1075,8 @@
}
return VerifyCredentialResponse.fromGateKeeperResponse(response);
} else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
- if (!isWeaverAvailable()) {
+ final IWeaver weaver = getWeaverService();
+ if (weaver == null) {
Slog.e(TAG, "No weaver service to verify SP-based persistent data credential");
return VerifyCredentialResponse.ERROR;
}
@@ -1056,7 +1084,8 @@
byte[] stretchedLskf = stretchLskf(userCredential, pwd);
int weaverSlot = persistentData.userId;
- return weaverVerify(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf)).stripPayload();
+ return weaverVerify(weaver, weaverSlot,
+ stretchedLskfToWeaverKey(stretchedLskf)).stripPayload();
} else {
Slog.e(TAG, "persistentData.type must be TYPE_SP_GATEKEEPER or TYPE_SP_WEAVER, but is "
+ persistentData.type);
@@ -1209,7 +1238,7 @@
TokenData tokenData = new TokenData();
tokenData.mType = type;
final byte[] secdiscardable = SecureRandomUtils.randomBytes(SECDISCARDABLE_LENGTH);
- if (isWeaverAvailable()) {
+ if (getWeaverService() != null) {
tokenData.weaverSecret = SecureRandomUtils.randomBytes(mWeaverConfig.valueSize);
tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret,
PERSONALIZATION_WEAVER_TOKEN, secdiscardable);
@@ -1252,10 +1281,11 @@
return false;
}
Slogf.i(TAG, "Creating token-based protector %016x for user %d", tokenHandle, userId);
- if (isWeaverAvailable()) {
+ final IWeaver weaver = getWeaverService();
+ if (weaver != null) {
int slot = getNextAvailableWeaverSlot();
Slogf.i(TAG, "Using Weaver slot %d for new token-based protector", slot);
- if (weaverEnroll(slot, null, tokenData.weaverSecret) == null) {
+ if (weaverEnroll(weaver, slot, null, tokenData.weaverSecret) == null) {
Slog.e(TAG, "Failed to enroll weaver secret when activating token");
return false;
}
@@ -1344,12 +1374,14 @@
int weaverSlot = loadWeaverSlot(protectorId, userId);
if (weaverSlot != INVALID_WEAVER_SLOT) {
// Protector uses Weaver to verify the LSKF
- if (!isWeaverAvailable()) {
+ final IWeaver weaver = getWeaverService();
+ if (weaver == null) {
Slog.e(TAG, "Protector uses Weaver, but Weaver is unavailable");
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
- result.gkResponse = weaverVerify(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf));
+ result.gkResponse = weaverVerify(weaver, weaverSlot,
+ stretchedLskfToWeaverKey(stretchedLskf));
if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
return result;
}
@@ -1517,12 +1549,13 @@
}
int slotId = loadWeaverSlot(protectorId, userId);
if (slotId != INVALID_WEAVER_SLOT) {
- if (!isWeaverAvailable()) {
+ final IWeaver weaver = getWeaverService();
+ if (weaver == null) {
Slog.e(TAG, "Protector uses Weaver, but Weaver is unavailable");
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
- VerifyCredentialResponse response = weaverVerify(slotId, null);
+ VerifyCredentialResponse response = weaverVerify(weaver, slotId, null);
if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
response.getGatekeeperHAT() == null) {
Slog.e(TAG,
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 794c17d..63dc59c 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -925,6 +925,17 @@
return;
}
+ Slog.i(
+ TAG,
+ TextUtils.formatSimple(
+ "requestCreateSessionWithRouter2 | router: %s(id: %d), old session id: %s,"
+ + " new session's route id: %s, request id: %d",
+ routerRecord.mPackageName,
+ routerRecord.mRouterId,
+ oldSession.getId(),
+ route.getId(),
+ requestId));
+
if (managerRequestId != MediaRoute2ProviderService.REQUEST_ID_NONE) {
ManagerRecord manager = routerRecord.mUserRecord.mHandler.findManagerWithId(
toRequesterId(managerRequestId));
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 4974cd9..4963dd4 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -632,7 +632,8 @@
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
null /*splashScreenTheme*/,
- 0 /*firstInstallTime*/);
+ 0 /*firstInstallTime*/,
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
}
mPm.mSettings.writeKernelMappingLPr(ps);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8ce06d4..af6c1a2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -29,6 +29,7 @@
import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
import static android.os.Process.INVALID_UID;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
@@ -712,8 +713,7 @@
private final PackageObserverHelper mPackageObserverHelper = new PackageObserverHelper();
@NonNull
- private final PackageMonitorCallbackHelper mPackageMonitorCallbackHelper =
- new PackageMonitorCallbackHelper();
+ private final PackageMonitorCallbackHelper mPackageMonitorCallbackHelper;
private final ModuleInfoProvider mModuleInfoProvider;
@@ -1837,6 +1837,7 @@
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
mStorageEventHelper = testParams.storageEventHelper;
+ mPackageMonitorCallbackHelper = testParams.packageMonitorCallbackHelper;
registerObservers(false);
invalidatePackageInfoCache();
@@ -1977,6 +1978,7 @@
mDomainVerificationManager.setConnection(mDomainVerificationConnection);
mBroadcastHelper = new BroadcastHelper(mInjector);
+ mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper(mInjector);
mAppDataHelper = new AppDataHelper(this);
mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
@@ -2498,13 +2500,22 @@
@NonNull
private String getRequiredServicesExtensionPackageLPr(@NonNull Computer computer) {
- String servicesExtensionPackage =
- ensureSystemPackageName(computer,
- mContext.getString(R.string.config_servicesExtensionPackage));
+ String configServicesExtensionPackage = mContext.getString(
+ R.string.config_servicesExtensionPackage);
+ if (TextUtils.isEmpty(configServicesExtensionPackage)) {
+ throw new RuntimeException(
+ "Required services extension package failed due to "
+ + "config_servicesExtensionPackage is empty.");
+ }
+ String servicesExtensionPackage = ensureSystemPackageName(computer,
+ configServicesExtensionPackage);
if (TextUtils.isEmpty(servicesExtensionPackage)) {
throw new RuntimeException(
- "Required services extension package is missing, check "
- + "config_servicesExtensionPackage.");
+ "Required services extension package is missing, "
+ + "config_servicesExtensionPackage had defined with "
+ + configServicesExtensionPackage
+ + ", but can not find the package info on the system image, check if "
+ + "the package has a problem.");
}
return servicesExtensionPackage;
}
@@ -5128,6 +5139,20 @@
}
@Override
+ @PackageManager.UserMinAspectRatio
+ public int getUserMinAspectRatio(@NonNull String packageName, int userId) {
+ final Computer snapshot = snapshotComputer();
+ final int callingUid = Binder.getCallingUid();
+ snapshot.enforceCrossUserPermission(
+ callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "getUserMinAspectRatio");
+ final PackageStateInternal packageState = snapshot
+ .getPackageStateForInstalledAndFiltered(packageName, callingUid, userId);
+ return packageState == null ? USER_MIN_ASPECT_RATIO_UNSET
+ : packageState.getUserStateOrDefault(userId).getMinAspectRatio();
+ }
+
+ @Override
public Bundle getSuspendedPackageAppExtras(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
final Computer snapshot = snapshot();
@@ -6092,6 +6117,32 @@
return true;
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.INSTALL_PACKAGES)
+ @Override
+ public void setUserMinAspectRatio(@NonNull String packageName, int userId,
+ @PackageManager.UserMinAspectRatio int aspectRatio) {
+ setUserMinAspectRatio_enforcePermission();
+ final int callingUid = Binder.getCallingUid();
+ final Computer snapshot = snapshotComputer();
+ snapshot.enforceCrossUserPermission(callingUid, userId,
+ false /* requireFullPermission */, false /* checkShell */,
+ "setUserMinAspectRatio");
+ enforceOwnerRights(snapshot, packageName, callingUid);
+
+ final PackageStateInternal packageState = snapshot
+ .getPackageStateForInstalledAndFiltered(packageName, callingUid, userId);
+ if (packageState == null) {
+ return;
+ }
+
+ if (packageState.getUserStateOrDefault(userId).getMinAspectRatio() == aspectRatio) {
+ return;
+ }
+
+ commitPackageStateMutation(null, packageName, state ->
+ state.userState(userId).setMinAspectRatio(aspectRatio));
+ }
+
@Override
@SuppressWarnings("GuardedBy")
public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 50711de..ca57209 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -123,4 +123,5 @@
public Set<String> initialNonStoppedSystemPackages = new ArraySet<>();
public boolean shouldStopSystemPackagesByDefault;
public FreeStorageHelper freeStorageHelper;
+ public PackageMonitorCallbackHelper packageMonitorCallbackHelper;
}
diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
index 9334595..c582321 100644
--- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
@@ -27,6 +27,7 @@
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -45,6 +46,12 @@
private final Object mLock = new Object();
final IActivityManager mActivityManager = ActivityManager.getService();
+ final Handler mHandler;
+
+ PackageMonitorCallbackHelper(PackageManagerServiceInjector injector) {
+ mHandler = injector.getHandler();
+ }
+
@NonNull
@GuardedBy("mLock")
private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>();
@@ -149,13 +156,14 @@
intent.putExtra(Intent.EXTRA_UID, uid);
}
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- callbacks.broadcast((callback, user) -> {
+
+ mHandler.post(() -> callbacks.broadcast((callback, user) -> {
int registerUserId = (int) user;
if ((registerUserId != UserHandle.USER_ALL) && (registerUserId != userId)) {
return;
}
invokeCallback(callback, intent);
- });
+ }));
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 2411820..3e9ccac 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -875,7 +875,7 @@
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
int installReason, int uninstallReason,
String harmfulAppWarning, String splashScreenTheme,
- long firstInstallTime) {
+ long firstInstallTime, int aspectRatio) {
modifyUserState(userId)
.setSuspendParams(suspendParams)
.setCeDataInode(ceDataInode)
@@ -894,7 +894,8 @@
.setVirtualPreload(virtualPreload)
.setHarmfulAppWarning(harmfulAppWarning)
.setSplashScreenTheme(splashScreenTheme)
- .setFirstInstallTimeMillis(firstInstallTime);
+ .setFirstInstallTimeMillis(firstInstallTime)
+ .setMinAspectRatio(aspectRatio);
onChanged();
}
@@ -912,7 +913,7 @@
? null : otherState.getDisabledComponentsNoCopy().untrackedStorage(),
otherState.getInstallReason(), otherState.getUninstallReason(),
otherState.getHarmfulAppWarning(), otherState.getSplashScreenTheme(),
- otherState.getFirstInstallTimeMillis());
+ otherState.getFirstInstallTimeMillis(), otherState.getMinAspectRatio());
}
WatchedArraySet<String> getEnabledComponents(int userId) {
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 2596006..a8cdef4 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -83,6 +83,8 @@
// Append targetSdkVersion=n to existing seinfo label where n is the app's targetSdkVersion
private static final String TARGETSDKVERSION_STR = ":targetSdkVersion=";
+ private static final String PARTITION_STR = ":partition=";
+
/**
* Allows opt-in to the latest targetSdkVersion enforced changes without changing target SDK.
* Turning this change on for an app targeting the latest SDK or higher is a no-op.
@@ -373,15 +375,33 @@
return pkg.getTargetSdkVersion();
}
+ private static String getPartition(PackageState state) {
+ if (state.isSystemExt()) {
+ return "system_ext";
+ } else if (state.isProduct()) {
+ return "product";
+ } else if (state.isVendor()) {
+ return "vendor";
+ } else if (state.isOem()) {
+ return "oem";
+ } else if (state.isOdm()) {
+ return "odm";
+ } else if (state.isSystem()) {
+ return "system";
+ }
+ return "";
+ }
+
/**
* Selects a security label to a package based on input parameters and the seinfo tag taken
* from a matched policy. All signature based policy stanzas are consulted and, if no match
* is found, the default seinfo label of 'default' is used. The security label is attached to
* the ApplicationInfo instance of the package.
*
- * @param pkg object representing the package to be labeled.
- * @param sharedUser if the app shares a sharedUserId, then this has the shared setting.
- * @param compatibility the PlatformCompat service to ask about state of compat changes.
+ * @param packageState {@link PackageState} object representing the package to be labeled.
+ * @param pkg {@link AndroidPackage} object representing the package to be labeled.
+ * @param sharedUser if the app shares a sharedUserId, then this has the shared setting.
+ * @param compatibility the PlatformCompat service to ask about state of compat changes.
* @return String representing the resulting seinfo.
*/
public static String getSeInfo(@NonNull PackageState packageState, @NonNull AndroidPackage pkg,
@@ -393,7 +413,7 @@
final boolean isPrivileged =
(sharedUser != null) ? sharedUser.isPrivileged() | packageState.isPrivileged()
: packageState.isPrivileged();
- return getSeInfo(pkg, isPrivileged, targetSdkVersion);
+ return getSeInfo(packageState, pkg, isPrivileged, targetSdkVersion);
}
/**
@@ -402,15 +422,16 @@
* is found, the default seinfo label of 'default' is used. The security label is attached to
* the ApplicationInfo instance of the package.
*
- * @param pkg object representing the package to be labeled.
- * @param isPrivileged boolean.
+ * @param packageState {@link PackageState} object representing the package to be labeled.
+ * @param pkg {@link AndroidPackage} object representing the package to be labeled.
+ * @param isPrivileged boolean.
* @param targetSdkVersion int. If this pkg runs as a sharedUser, targetSdkVersion is the
* greater of: lowest targetSdk for all pkgs in the sharedUser, or
* MINIMUM_TARGETSDKVERSION.
* @return String representing the resulting seinfo.
*/
- public static String getSeInfo(AndroidPackage pkg, boolean isPrivileged,
- int targetSdkVersion) {
+ public static String getSeInfo(PackageState packageState, AndroidPackage pkg,
+ boolean isPrivileged, int targetSdkVersion) {
String seInfo = null;
synchronized (sPolicies) {
if (!sPolicyRead) {
@@ -437,8 +458,13 @@
seInfo += TARGETSDKVERSION_STR + targetSdkVersion;
+ String partition = getPartition(packageState);
+ if (!partition.isEmpty()) {
+ seInfo += PARTITION_STR + partition;
+ }
+
if (DEBUG_POLICY_INSTALL) {
- Slog.i(TAG, "package (" + pkg.getPackageName() + ") labeled with "
+ Slog.i(TAG, "package (" + packageState.getPackageName() + ") labeled with "
+ "seinfo=" + seInfo);
}
return seInfo;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index be03125..87f9126 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -352,6 +352,7 @@
private static final String ATTR_VIRTUAL_PRELOAD = "virtual-preload";
private static final String ATTR_HARMFUL_APP_WARNING = "harmful-app-warning";
private static final String ATTR_SPLASH_SCREEN_THEME = "splash-screen-theme";
+ private static final String ATTR_MIN_ASPECT_RATIO = "min-aspect-ratio";
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_BUILD_FINGERPRINT = "buildFingerprint";
@@ -1127,7 +1128,8 @@
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
null /*splashscreenTheme*/,
- 0 /*firstInstallTime*/
+ 0 /*firstInstallTime*/,
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET
);
}
}
@@ -1794,7 +1796,8 @@
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
null /* splashScreenTheme*/,
- 0 /*firstInstallTime*/
+ 0 /*firstInstallTime*/,
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET
);
}
return;
@@ -1889,6 +1892,9 @@
ATTR_SPLASH_SCREEN_THEME);
final long firstInstallTime = parser.getAttributeLongHex(null,
ATTR_FIRST_INSTALL_TIME, 0);
+ final int minAspectRatio = parser.getAttributeInt(null,
+ ATTR_MIN_ASPECT_RATIO,
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
@@ -1965,7 +1971,8 @@
enabledCaller, enabledComponents, disabledComponents, installReason,
uninstallReason, harmfulAppWarning, splashScreenTheme,
firstInstallTime != 0 ? firstInstallTime :
- origFirstInstallTimes.getOrDefault(name, 0L));
+ origFirstInstallTimes.getOrDefault(name, 0L),
+ minAspectRatio);
mDomainVerificationManager.setLegacyUserState(name, userId, verifState);
} else if (tagName.equals("preferred-activities")) {
@@ -2265,6 +2272,11 @@
serializer.attribute(null, ATTR_SPLASH_SCREEN_THEME,
ustate.getSplashScreenTheme());
}
+ if (ustate.getMinAspectRatio()
+ != PackageManager.USER_MIN_ASPECT_RATIO_UNSET) {
+ serializer.attributeInt(null, ATTR_MIN_ASPECT_RATIO,
+ ustate.getMinAspectRatio());
+ }
if (ustate.isSuspended()) {
for (int i = 0; i < ustate.getSuspendParams().size(); i++) {
final String suspendingPackage = ustate.getSuspendParams().keyAt(i);
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index a037ae8..9376259 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -285,7 +285,7 @@
continue;
}
final boolean isPrivileged = isPrivileged() | ps.isPrivileged();
- ps.getPkgState().setOverrideSeInfo(SELinuxMMAC.getSeInfo(ps.getPkg(), isPrivileged,
+ ps.getPkgState().setOverrideSeInfo(SELinuxMMAC.getSeInfo(ps, ps.getPkg(), isPrivileged,
seInfoTargetSdkVersion));
onChanged();
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 5b3514c..710e0b7 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -15,6 +15,7 @@
*/
package com.android.server.pm;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
import android.Manifest.permission;
@@ -24,6 +25,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IUidObserver;
import android.app.IUriGrantsManager;
@@ -4407,8 +4409,11 @@
return;
}
try {
+ ActivityOptions options = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_DENIED);
intentSender.sendIntent(mContext, /* code= */ 0, extras,
- /* onFinished=*/ null, /* handler= */ null);
+ /* onFinished=*/ null, /* handler= */ null, null, options.toBundle());
} catch (SendIntentException e) {
Slog.w(TAG, "sendIntent failed().", e);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 29c2f27..e9c6511 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1037,7 +1037,7 @@
final UserData userData = mUsers.valueAt(i);
final int userId = userData.info.id;
if (userId != currentUser && userData.info.isFull() && !userData.info.partial
- && !mRemovingUserIds.get(userId)) {
+ && userData.info.isEnabled() && !mRemovingUserIds.get(userId)) {
final long userEnteredTime = userData.mLastEnteredForegroundTimeMillis;
if (userEnteredTime > latestEnteredTime) {
latestEnteredTime = userEnteredTime;
@@ -5636,8 +5636,14 @@
}
}
- @GuardedBy("mUsersLock")
@VisibleForTesting
+ void addRemovingUserId(@UserIdInt int userId) {
+ synchronized (mUsersLock) {
+ addRemovingUserIdLocked(userId);
+ }
+ }
+
+ @GuardedBy("mUsersLock")
void addRemovingUserIdLocked(@UserIdInt int userId) {
// We remember deleted user IDs to prevent them from being
// reused during the current boot; they can still be reused
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 9ebb8d6..7bdcd68 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -633,4 +633,12 @@
public boolean isCommunalProfile() {
return UserManager.isUserTypeCommunalProfile(mName);
}
+
+ /**
+ * Returns whether the user type is a private profile
+ * (i.e. {@link UserManager#USER_TYPE_PROFILE_PRIVATE}).
+ */
+ public boolean isPrivateProfile() {
+ return UserManager.isUserTypePrivateProfile(mName);
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 1569e06..f7967c0 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -35,6 +35,7 @@
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
import static android.os.UserManager.USER_TYPE_PROFILE_COMMUNAL;
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
+import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
import static android.os.UserManager.USER_TYPE_PROFILE_TEST;
import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
@@ -108,6 +109,7 @@
builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless());
builders.put(USER_TYPE_PROFILE_CLONE, getDefaultTypeProfileClone());
builders.put(USER_TYPE_PROFILE_COMMUNAL, getDefaultTypeProfileCommunal());
+ builders.put(USER_TYPE_PROFILE_PRIVATE, getDefaultTypeProfilePrivate());
if (Build.IS_DEBUGGABLE) {
builders.put(USER_TYPE_PROFILE_TEST, getDefaultTypeProfileTest());
}
@@ -264,6 +266,39 @@
}
/**
+ * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_PRIVATE}
+ * configuration.
+ */
+ private static UserTypeDetails.Builder getDefaultTypeProfilePrivate() {
+ return new UserTypeDetails.Builder()
+ .setName(USER_TYPE_PROFILE_PRIVATE)
+ .setBaseType(FLAG_PROFILE)
+ .setMaxAllowedPerParent(1)
+ .setLabel(0)
+ .setIconBadge(com.android.internal.R.drawable.ic_test_icon_badge_experiment)
+ .setBadgePlain(com.android.internal.R.drawable.ic_test_badge_experiment)
+ .setBadgeNoBackground(com.android.internal.R.drawable.ic_test_badge_no_background)
+ .setStatusBarIcon(com.android.internal.R.drawable.ic_test_badge_experiment)
+ .setBadgeLabels(
+ com.android.internal.R.string.managed_profile_label_badge,
+ com.android.internal.R.string.managed_profile_label_badge_2,
+ com.android.internal.R.string.managed_profile_label_badge_3)
+ .setBadgeColors(
+ com.android.internal.R.color.profile_badge_2)
+ .setDarkThemeBadgeColors(
+ com.android.internal.R.color.profile_badge_2_dark)
+ .setDefaultRestrictions(getDefaultProfileRestrictions())
+ .setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings())
+ .setDefaultUserProperties(new UserProperties.Builder()
+ .setStartWithParent(true)
+ .setCredentialShareableWithParent(false)
+ .setMediaSharedWithParent(false)
+ .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
+ .setCrossProfileIntentFilterAccessControl(
+ UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM));
+ }
+
+ /**
* Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_SECONDARY}
* configuration.
*/
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
index 91a25db3..3d056e8 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
@@ -377,6 +377,8 @@
private final int mUninstallReason;
@Nullable
private final String mSplashScreenTheme;
+ @PackageManager.UserMinAspectRatio
+ private final int mMinAspectRatio;
private final long mFirstInstallTimeMillis;
private UserStateImpl(@NonNull PackageUserState userState) {
@@ -392,6 +394,7 @@
mSharedLibraryOverlayPaths = userState.getSharedLibraryOverlayPaths();
mUninstallReason = userState.getUninstallReason();
mSplashScreenTheme = userState.getSplashScreenTheme();
+ mMinAspectRatio = userState.getMinAspectRatio();
setBoolean(Booleans.HIDDEN, userState.isHidden());
setBoolean(Booleans.INSTALLED, userState.isInstalled());
setBoolean(Booleans.INSTANT_APP, userState.isInstantApp());
@@ -543,6 +546,11 @@
}
@DataClass.Generated.Member
+ public @PackageManager.UserMinAspectRatio int getMinAspectRatio() {
+ return mMinAspectRatio;
+ }
+
+ @DataClass.Generated.Member
public long getFirstInstallTimeMillis() {
return mFirstInstallTimeMillis;
}
@@ -554,10 +562,10 @@
}
@DataClass.Generated(
- time = 1671671043891L,
+ time = 1687938966108L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate final long mFirstInstallTimeMillis\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate final @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate final long mFirstInstallTimeMillis\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserState.java b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
index 2048d65..f75d214 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
@@ -217,4 +217,12 @@
*/
@Nullable
String getSplashScreenTheme();
+
+ /**
+ * @return the min aspect ratio setting of the package which by default is unset
+ * unless it has been set by the user
+ * @hide
+ */
+ @PackageManager.UserMinAspectRatio
+ int getMinAspectRatio();
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
index 73fb672..1fb12a8 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
@@ -136,6 +136,11 @@
}
@Override
+ public @PackageManager.UserMinAspectRatio int getMinAspectRatio() {
+ return PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
+ }
+
+ @Override
public long getFirstInstallTimeMillis() {
return 0;
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index e8e2d41..d911ac1 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -83,6 +83,9 @@
@Nullable
private String mSplashScreenTheme;
+ @PackageManager.UserMinAspectRatio
+ private int mMinAspectRatio = PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
+
/**
* Suspending package to suspend params
*/
@@ -146,6 +149,7 @@
mHarmfulAppWarning = other.mHarmfulAppWarning;
mLastDisableAppCaller = other.mLastDisableAppCaller;
mSplashScreenTheme = other.mSplashScreenTheme;
+ mMinAspectRatio = other.mMinAspectRatio;
mSuspendParams = other.mSuspendParams == null ? null : other.mSuspendParams.snapshot();
mComponentLabelIconOverrideMap = other.mComponentLabelIconOverrideMap == null
? null : other.mComponentLabelIconOverrideMap.snapshot();
@@ -508,6 +512,19 @@
}
/**
+ * Sets user min aspect ratio override value
+ * @see PackageManager.UserMinAspectRatio
+ */
+ public @NonNull PackageUserStateImpl setMinAspectRatio(
+ @PackageManager.UserMinAspectRatio int value) {
+ mMinAspectRatio = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ PackageManager.UserMinAspectRatio.class, null, mMinAspectRatio);
+ onChanged();
+ return this;
+ }
+
+ /**
* Suspending package to suspend params
*/
public @NonNull PackageUserStateImpl setSuspendParams(
@@ -679,6 +696,11 @@
return mSplashScreenTheme;
}
+ @DataClass.Generated.Member
+ public @PackageManager.UserMinAspectRatio int getMinAspectRatio() {
+ return mMinAspectRatio;
+ }
+
/**
* Suspending package to suspend params
*/
@@ -766,6 +788,7 @@
&& Objects.equals(mOverlayPaths, that.mOverlayPaths)
&& Objects.equals(mSharedLibraryOverlayPaths, that.mSharedLibraryOverlayPaths)
&& Objects.equals(mSplashScreenTheme, that.mSplashScreenTheme)
+ && mMinAspectRatio == that.mMinAspectRatio
&& Objects.equals(mSuspendParams, that.mSuspendParams)
&& Objects.equals(mComponentLabelIconOverrideMap, that.mComponentLabelIconOverrideMap)
&& mFirstInstallTimeMillis == that.mFirstInstallTimeMillis
@@ -798,6 +821,7 @@
_hash = 31 * _hash + Objects.hashCode(mOverlayPaths);
_hash = 31 * _hash + Objects.hashCode(mSharedLibraryOverlayPaths);
_hash = 31 * _hash + Objects.hashCode(mSplashScreenTheme);
+ _hash = 31 * _hash + mMinAspectRatio;
_hash = 31 * _hash + Objects.hashCode(mSuspendParams);
_hash = 31 * _hash + Objects.hashCode(mComponentLabelIconOverrideMap);
_hash = 31 * _hash + Long.hashCode(mFirstInstallTimeMillis);
@@ -807,10 +831,10 @@
}
@DataClass.Generated(
- time = 1686952839807L,
+ time = 1687938397579L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
- inputSignatures = "protected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+ inputSignatures = "protected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
index 8125b0f..8430cf7 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
@@ -439,6 +439,16 @@
}
return null;
}
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setMinAspectRatio(
+ @PackageManager.UserMinAspectRatio int aspectRatio) {
+ if (mUserState != null) {
+ mUserState.setMinAspectRatio(aspectRatio);
+ }
+ return this;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
index 11d6d97..0c6c672 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageManager;
import android.content.pm.overlay.OverlayPaths;
+import com.android.server.pm.pkg.PackageUserStateImpl;
import com.android.server.pm.pkg.SuspendParams;
public interface PackageUserStateWrite {
@@ -68,4 +69,8 @@
@NonNull
PackageUserStateWrite setComponentLabelIcon(@NonNull ComponentName componentName,
@Nullable String nonLocalizedLabel, @Nullable Integer icon);
+
+ /** @see PackageUserStateImpl#setMinAspectRatio(int) */
+ @NonNull
+ PackageUserStateWrite setMinAspectRatio(@PackageManager.UserMinAspectRatio int aspectRatio);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a292bf3..fc88776 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -724,8 +724,11 @@
finishKeyguardDrawn();
break;
case MSG_WINDOW_MANAGER_DRAWN_COMPLETE:
- if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mWindowManagerDrawComplete");
- finishWindowsDrawn(msg.arg1);
+ final int displayId = msg.arg1;
+ if (DEBUG_WAKEUP) Slog.w(TAG, "All windows drawn on display " + displayId);
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
+ TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, displayId /* cookie */);
+ finishWindowsDrawn(displayId);
break;
case MSG_HIDE_BOOT_MESSAGE:
handleHideBootMessage();
@@ -5135,15 +5138,10 @@
// ... eventually calls finishWindowsDrawn which will finalize our screen turn on
// as well as enabling the orientation change logic/sensor.
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
- TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
- mWindowManagerInternal.waitForAllWindowsDrawn(() -> {
- if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for every display");
- mHandler.sendMessage(mHandler.obtainMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE,
- INVALID_DISPLAY, 0));
-
- Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
- TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
- }, WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY);
+ TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, INVALID_DISPLAY /* cookie */);
+ mWindowManagerInternal.waitForAllWindowsDrawn(mHandler.obtainMessage(
+ MSG_WINDOW_MANAGER_DRAWN_COMPLETE, INVALID_DISPLAY, 0),
+ WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY);
}
// Called on the DisplayManager's DisplayPowerController thread.
@@ -5223,15 +5221,10 @@
mScreenOnListeners.put(displayId, screenOnListener);
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
- TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
- mWindowManagerInternal.waitForAllWindowsDrawn(() -> {
- if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for display: " + displayId);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE,
- displayId, 0));
-
- Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
- TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
- }, WAITING_FOR_DRAWN_TIMEOUT, displayId);
+ TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, displayId /* cookie */);
+ mWindowManagerInternal.waitForAllWindowsDrawn(mHandler.obtainMessage(
+ MSG_WINDOW_MANAGER_DRAWN_COMPLETE, displayId, 0),
+ WAITING_FOR_DRAWN_TIMEOUT, displayId);
}
}
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index 9c3b38a..b999bbb3 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -314,7 +314,9 @@
if (eventTime < mLastDownTime + mActiveRule.getVeryLongPressTimeoutMs()) {
mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
} else {
- mHandledByLongPress = mActiveRule.supportVeryLongPress();
+ // If long press or very long press (~3.5s) had been handled, we should skip the
+ // short press behavior.
+ mHandledByLongPress |= mActiveRule.supportVeryLongPress();
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 17b460b..25a0a52 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -3158,7 +3158,10 @@
if (which == FLAG_SYSTEM && systemIsStatic && systemIsBoth) {
Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
+ " updating system wallpaper");
- migrateStaticSystemToLockWallpaperLocked(userId);
+ if (!migrateStaticSystemToLockWallpaperLocked(userId)
+ && !isLockscreenLiveWallpaperEnabled()) {
+ which |= FLAG_LOCK;
+ }
}
wallpaper = getWallpaperSafeLocked(userId, which);
@@ -3186,13 +3189,13 @@
}
}
- private void migrateStaticSystemToLockWallpaperLocked(int userId) {
+ private boolean migrateStaticSystemToLockWallpaperLocked(int userId) {
WallpaperData sysWP = mWallpaperMap.get(userId);
if (sysWP == null) {
if (DEBUG) {
Slog.i(TAG, "No system wallpaper? Not tracking for lock-only");
}
- return;
+ return true;
}
// We know a-priori that there is no lock-only wallpaper currently
@@ -3219,9 +3222,12 @@
SELinux.restorecon(lockWP.getWallpaperFile());
mLastLockWallpaper = lockWP;
}
+ return true;
} catch (ErrnoException e) {
- Slog.e(TAG, "Can't migrate system wallpaper: " + e.getMessage());
+ // can happen when migrating default wallpaper (which is not stored in wallpaperFile)
+ Slog.w(TAG, "Couldn't migrate system wallpaper: " + e.getMessage());
clearWallpaperBitmaps(lockWP);
+ return false;
}
}
@@ -3433,7 +3439,9 @@
// therefore it's a shared system+lock image that we need to migrate.
Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
+ "updating system wallpaper");
- migrateStaticSystemToLockWallpaperLocked(userId);
+ if (!migrateStaticSystemToLockWallpaperLocked(userId)) {
+ which |= FLAG_LOCK;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 0a47fe0..20595ea 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -128,6 +128,21 @@
}
}
+ /** Notifies that the pointer location configuration has changed. */
+ @Override
+ public void notifyPointerLocationChanged(boolean pointerLocationEnabled) {
+ if (mService.mPointerLocationEnabled == pointerLocationEnabled) {
+ return;
+ }
+
+ synchronized (mService.mGlobalLock) {
+ mService.mPointerLocationEnabled = pointerLocationEnabled;
+ mService.mRoot.forAllDisplayPolicies(
+ p -> p.setPointerLocationEnabled(mService.mPointerLocationEnabled)
+ );
+ }
+ }
+
/** Notifies that the lid switch changed state. */
@Override
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 4620266..835c92d 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -21,12 +21,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
-import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
-import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
-import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
import static android.view.InsetsSource.ID_IME;
-import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
@@ -39,16 +34,13 @@
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.IBinder;
import android.util.SparseArray;
-import android.view.InsetsAnimationControlCallbacks;
-import android.view.InsetsAnimationControlImpl;
-import android.view.InsetsAnimationControlRunner;
import android.view.InsetsController;
import android.view.InsetsFrameProvider;
import android.view.InsetsSource;
-import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.InternalInsetsAnimationController;
import android.view.SurfaceControl;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.WindowInsets;
@@ -56,15 +48,15 @@
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
-import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.DisplayThread;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.PrintWriter;
+import java.util.List;
/**
* Policy that implements who gets control over the windows generating insets.
@@ -79,48 +71,19 @@
private final DisplayContent mDisplayContent;
private final DisplayPolicy mPolicy;
- /** For resetting visibilities of insets sources. */
- private final InsetsControlTarget mDummyControlTarget = new InsetsControlTarget() {
+ /** Used to show system bars transiently. This won't affect the layout. */
+ private final InsetsControlTarget mTransientControlTarget;
- @Override
- public void notifyInsetsControlChanged() {
- boolean hasLeash = false;
- final InsetsSourceControl[] controls =
- mStateController.getControlsForDispatch(this);
- if (controls == null) {
- return;
- }
- for (InsetsSourceControl control : controls) {
- if (isTransient(control.getType())) {
- // The visibilities of transient bars will be handled with animations.
- continue;
- }
- final SurfaceControl leash = control.getLeash();
- if (leash != null) {
- hasLeash = true;
-
- // We use alpha to control the visibility here which aligns the logic at
- // SurfaceAnimator.createAnimationLeash
- final boolean visible =
- (control.getType() & WindowInsets.Type.defaultVisible()) != 0;
- mDisplayContent.getPendingTransaction().setAlpha(leash, visible ? 1f : 0f);
- }
- }
- if (hasLeash) {
- mDisplayContent.scheduleAnimation();
- }
- }
- };
+ /** Used to show system bars permanently. This will affect the layout. */
+ private final InsetsControlTarget mPermanentControlTarget;
private WindowState mFocusedWin;
private final BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
private final BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
private @InsetsType int mShowingTransientTypes;
private @InsetsType int mForcedShowingTypes;
- private boolean mAnimatingShown;
private final boolean mHideNavBarForKeyboard;
- private final float[] mTmpFloat9 = new float[9];
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
mStateController = stateController;
@@ -128,6 +91,10 @@
mPolicy = displayContent.getDisplayPolicy();
final Resources r = mPolicy.getContext().getResources();
mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard);
+ mTransientControlTarget = new ControlTarget(
+ stateController, displayContent.mWmService.mH, "TransientControlTarget");
+ mPermanentControlTarget = new ControlTarget(
+ stateController, displayContent.mWmService.mH, "PermanentControlTarget");
}
/** Updates the target which can control system bars. */
@@ -144,13 +111,13 @@
final WindowState topApp = mPolicy.getTopFullscreenOpaqueWindow();
mStateController.onBarControlTargetChanged(
statusControlTarget,
- statusControlTarget == mDummyControlTarget
+ statusControlTarget == mTransientControlTarget
? getStatusControlTarget(focusedWin, true /* fake */)
: statusControlTarget == notificationShade
? getStatusControlTarget(topApp, true /* fake */)
: null,
navControlTarget,
- navControlTarget == mDummyControlTarget
+ navControlTarget == mTransientControlTarget
? getNavControlTarget(focusedWin, true /* fake */)
: navControlTarget == notificationShade
? getNavControlTarget(topApp, true /* fake */)
@@ -200,19 +167,19 @@
mFocusedWin,
(showingTransientTypes & (Type.statusBars() | Type.navigationBars())) != 0,
isGestureOnSystemBar);
-
- // The leashes can be created while updating bar control target. The surface transaction
- // of the new leashes might not be applied yet. The callback posted here ensures we can
- // get the valid leashes because the surface transaction will be applied in the next
- // animation frame which will be triggered if a new leash is created.
- mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> {
- synchronized (mDisplayContent.mWmService.mGlobalLock) {
- startAnimation(true /* show */, null /* callback */);
- }
- });
}
}
+ @VisibleForTesting
+ InsetsControlTarget getTransientControlTarget() {
+ return mTransientControlTarget;
+ }
+
+ @VisibleForTesting
+ InsetsControlTarget getPermanentControlTarget() {
+ return mPermanentControlTarget;
+ }
+
void hideTransient() {
if (mShowingTransientTypes == 0) {
return;
@@ -223,23 +190,8 @@
/* areVisible= */ false,
/* wereRevealedFromSwipeOnSystemBar= */ false);
- startAnimation(false /* show */, () -> {
- synchronized (mDisplayContent.mWmService.mGlobalLock) {
- final SparseArray<InsetsSourceProvider> providers =
- mStateController.getSourceProviders();
- for (int i = providers.size() - 1; i >= 0; i--) {
- final InsetsSourceProvider provider = providers.valueAt(i);
- if (!isTransient(provider.getSource().getType())) {
- continue;
- }
- // We are about to clear mShowingTransientTypes, we don't want the transient bar
- // can cause insets on the client. Restore the client visibility.
- provider.setClientVisible(false);
- }
- mShowingTransientTypes = 0;
- updateBarControlTarget(mFocusedWin);
- }
- });
+ mShowingTransientTypes = 0;
+ updateBarControlTarget(mFocusedWin);
}
boolean isTransient(@InsetsType int type) {
@@ -502,7 +454,7 @@
private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin,
boolean fake) {
if (!fake && isTransient(Type.statusBars())) {
- return mDummyControlTarget;
+ return mTransientControlTarget;
}
final WindowState notificationShade = mPolicy.getNotificationShade();
if (focusedWin == notificationShade) {
@@ -519,13 +471,13 @@
if (areTypesForciblyShowing(Type.statusBars())) {
// Status bar is forcibly shown. We don't want the client to control the status bar, and
// we will dispatch the real visibility of status bar to the client.
- return null;
+ return mPermanentControlTarget;
}
if (forceShowsStatusBarTransiently() && !fake) {
// Status bar is forcibly shown transiently, and its new visibility won't be
// dispatched to the client so that we can keep the layout stable. We will dispatch the
// fake control to the client, so that it can re-show the bar during this scenario.
- return mDummyControlTarget;
+ return mTransientControlTarget;
}
if (!canBeTopFullscreenOpaqueWindow(focusedWin)
&& mPolicy.topAppHidesSystemBar(Type.statusBars())
@@ -556,7 +508,7 @@
return null;
}
if (!fake && isTransient(Type.navigationBars())) {
- return mDummyControlTarget;
+ return mTransientControlTarget;
}
if (focusedWin == mPolicy.getNotificationShade()) {
// Notification shade has control anyways, no reason to force anything.
@@ -579,13 +531,13 @@
if (areTypesForciblyShowing(Type.navigationBars())) {
// Navigation bar is forcibly shown. We don't want the client to control the navigation
// bar, and we will dispatch the real visibility of navigation bar to the client.
- return null;
+ return mPermanentControlTarget;
}
if (forceShowsNavigationBarTransiently() && !fake) {
// Navigation bar is forcibly shown transiently, and its new visibility won't be
// dispatched to the client so that we can keep the layout stable. We will dispatch the
// fake control to the client, so that it can re-show the bar during this scenario.
- return mDummyControlTarget;
+ return mTransientControlTarget;
}
final WindowState notificationShade = mPolicy.getNotificationShade();
if (!canBeTopFullscreenOpaqueWindow(focusedWin)
@@ -662,34 +614,6 @@
&& (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
}
- @VisibleForTesting
- void startAnimation(boolean show, Runnable callback) {
- @InsetsType int typesReady = 0;
- final SparseArray<InsetsSourceControl> controlsReady = new SparseArray<>();
- final InsetsSourceControl[] controls =
- mStateController.getControlsForDispatch(mDummyControlTarget);
- if (controls == null) {
- if (callback != null) {
- DisplayThread.getHandler().post(callback);
- }
- return;
- }
- for (InsetsSourceControl control : controls) {
- if (isTransient(control.getType()) && control.getLeash() != null) {
- typesReady |= control.getType();
- controlsReady.put(control.getId(), new InsetsSourceControl(control));
- }
- }
- controlAnimationUnchecked(typesReady, controlsReady, show, callback);
- }
-
- private void controlAnimationUnchecked(int typesReady,
- SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback) {
- InsetsPolicyAnimationControlListener listener =
- new InsetsPolicyAnimationControlListener(show, callback, typesReady);
- listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show);
- }
-
private void dispatchTransientSystemBarsVisibilityChanged(
@Nullable WindowState focusedWindow,
boolean areVisible,
@@ -760,105 +684,138 @@
}
}
- private class InsetsPolicyAnimationControlListener extends
- InsetsController.InternalAnimationControlListener {
- Runnable mFinishCallback;
- InsetsPolicyAnimationControlCallbacks mControlCallbacks;
+ private static class ControlTarget implements InsetsControlTarget {
- InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) {
- super(show, false /* hasCallbacks */, types, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE,
- false /* disable */, 0 /* floatingImeBottomInsets */,
- null /* loggingListener */, null /* jankContext */);
- mFinishCallback = finishCallback;
- mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
+ private final InsetsState mState = new InsetsState();
+ private final InsetsController mInsetsController;
+ private final InsetsStateController mStateController;
+ private final String mName;
+
+ ControlTarget(InsetsStateController stateController, Handler handler, String name) {
+ mStateController = stateController;
+ mInsetsController = new InsetsController(new Host(handler, name));
+ mName = name;
}
@Override
- protected void onAnimationFinish() {
- super.onAnimationFinish();
- if (mFinishCallback != null) {
- DisplayThread.getHandler().post(mFinishCallback);
- }
+ public void notifyInsetsControlChanged() {
+ mState.set(mStateController.getRawInsetsState(), true /* copySources */);
+ mInsetsController.onStateChanged(mState);
+ mInsetsController.onControlsChanged(mStateController.getControlsForDispatch(this));
}
- private class InsetsPolicyAnimationControlCallbacks implements
- InsetsAnimationControlCallbacks {
- private InsetsAnimationControlImpl mAnimationControl = null;
- private InsetsPolicyAnimationControlListener mListener;
+ @Override
+ public String toString() {
+ return mName;
+ }
+ }
- InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) {
- mListener = listener;
+ private static class Host implements InsetsController.Host {
+
+ private final float[] mTmpFloat9 = new float[9];
+ private final Handler mHandler;
+ private final String mName;
+
+ Host(Handler handler, String name) {
+ mHandler = handler;
+ mName = name;
+ }
+
+ @Override
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ public void notifyInsetsChanged() { }
+
+ @Override
+ public void dispatchWindowInsetsAnimationPrepare(
+ @NonNull WindowInsetsAnimation animation) { }
+
+ @Override
+ public Bounds dispatchWindowInsetsAnimationStart(
+ @NonNull WindowInsetsAnimation animation,
+ @NonNull Bounds bounds) {
+ return bounds;
+ }
+
+ @Override
+ public WindowInsets dispatchWindowInsetsAnimationProgress(
+ @NonNull WindowInsets insets,
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
+ return insets;
+ }
+
+ @Override
+ public void dispatchWindowInsetsAnimationEnd(
+ @NonNull WindowInsetsAnimation animation) { }
+
+ @Override
+ public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... p) {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (int i = p.length - 1; i >= 0; i--) {
+ SyncRtSurfaceTransactionApplier.applyParams(t, p[i], mTmpFloat9);
}
+ t.apply();
+ t.close();
+ }
- private void controlAnimationUnchecked(int typesReady,
- SparseArray<InsetsSourceControl> controls, boolean show) {
- if (typesReady == 0) {
- // nothing to animate.
- return;
- }
- mAnimatingShown = show;
+ @Override
+ public void updateRequestedVisibleTypes(int types) { }
- final InsetsState state = mFocusedWin.getInsetsState();
+ @Override
+ public boolean hasAnimationCallbacks() {
+ return false;
+ }
- // We are about to playing the default animation. Passing a null frame indicates
- // the controlled types should be animated regardless of the frame.
- mAnimationControl = new InsetsAnimationControlImpl(controls,
- null /* frame */, state, mListener, typesReady, this,
- mListener.getDurationMs(), getInsetsInterpolator(),
- show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show
- ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
- : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
- null /* translator */, null /* statsToken */);
- SurfaceAnimationThread.getHandler().post(
- () -> mListener.onReady(mAnimationControl, typesReady));
- }
+ @Override
+ public void setSystemBarsAppearance(int appearance, int mask) { }
- /** Called on SurfaceAnimationThread without global WM lock held. */
- @Override
- public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) {
- if (mAnimationControl.applyChangeInsets(null /* outState */)) {
- mAnimationControl.finish(mAnimatingShown);
- }
- }
+ @Override
+ public int getSystemBarsAppearance() {
+ return 0;
+ }
- @Override
- public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
- // Nothing's needed here. Finish steps is handled in the listener
- // onAnimationFinished callback.
- }
+ @Override
+ public void setSystemBarsBehavior(int behavior) { }
- /** Called on SurfaceAnimationThread without global WM lock held. */
- @Override
- public void applySurfaceParams(
- final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- for (int i = params.length - 1; i >= 0; i--) {
- SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
- applyParams(t, surfaceParams, mTmpFloat9);
- }
- t.apply();
- t.close();
- }
+ @Override
+ public int getSystemBarsBehavior() {
+ return BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+ }
- // Since we don't push applySurfaceParams to a Handler-queue we don't need
- // to push release in this case.
- @Override
- public void releaseSurfaceControlFromRt(SurfaceControl sc) {
- sc.release();
- }
+ @Override
+ public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) {
+ surfaceControl.release();
+ }
- @Override
- public <T extends InsetsAnimationControlRunner & InternalInsetsAnimationController>
- void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types,
- WindowInsetsAnimation animation,
- Bounds bounds) {
- }
+ @Override
+ public void addOnPreDrawRunnable(Runnable r) { }
- @Override
- public void reportPerceptible(int types, boolean perceptible) {
- // No-op for now - only client windows report perceptibility for now, with policy
- // controllers assumed to always be perceptible.
- }
+ @Override
+ public void postInsetsAnimationCallback(Runnable r) { }
+
+ @Override
+ public InputMethodManager getInputMethodManager() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getRootViewTitle() {
+ return mName;
+ }
+
+ @Override
+ public int dipToPx(int dips) {
+ return 0;
+ }
+
+ @Nullable
+ @Override
+ public IBinder getWindowToken() {
+ return null;
}
}
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 4fc9557..814bd85 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -615,12 +615,8 @@
}
}
if (mParticipants.contains(wc)) return;
- // Wallpaper is like in a static drawn state unless display may have changes, so exclude
- // the case to reduce transition latency waiting for the unchanged wallpaper to redraw.
- final boolean needSync = (!isWallpaper(wc) || mParticipants.contains(wc.mDisplayContent))
- // Transient-hide may be hidden later, so no need to request redraw.
- && !isInTransientHide(wc);
- if (needSync) {
+ // Transient-hide may be hidden later, so no need to request redraw.
+ if (!isInTransientHide(wc)) {
mSyncEngine.addToSyncSet(mSyncId, wc);
}
ChangeInfo info = mChanges.get(wc);
@@ -1607,7 +1603,7 @@
// Since we created root-leash but no longer reference it from core, release it now
info.releaseAnimSurfaces();
- mController.mLoggerHandler.post(mLogger::logOnSend);
+ mLogger.logOnSendAsync(mController.mLoggerHandler);
if (mLogger.mInfo != null) {
mController.mTransitionTracer.logSentTransition(this, mTargets);
}
@@ -2274,7 +2270,7 @@
// transitions anyways).
return wc.getParent().asDisplayContent().getWindowingLayer();
}
- return wc.getParent().getSurfaceControl();
+ return wc.getParentSurfaceControl();
}
/**
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 79cb61b..881eddc 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -1434,7 +1434,7 @@
* Beside `mCreateWallTimeMs`, all times are elapsed times and will all be reported relative
* to when the transition was created.
*/
- static class Logger {
+ static class Logger implements Runnable {
long mCreateWallTimeMs;
long mCreateTimeNs;
long mRequestTimeNs;
@@ -1458,6 +1458,20 @@
return sb.toString();
}
+ void logOnSendAsync(Handler handler) {
+ handler.post(this);
+ }
+
+ @Override
+ public void run() {
+ try {
+ logOnSend();
+ } catch (Exception e) {
+ // In case TransitionRequestInfo#toString() accesses window container with race.
+ Slog.w(TAG, "Failed to log transition", e);
+ }
+ }
+
void logOnSend() {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "%s", buildOnSendLog());
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, " startWCT=%s", mStartWCT);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 9e7df00..805e7ff 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -30,6 +30,7 @@
import android.hardware.display.DisplayManagerInternal;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Message;
import android.util.Pair;
import android.view.ContentRecordingSession;
import android.view.Display;
@@ -515,12 +516,13 @@
* Invalidate all visible windows on a given display, and report back on the callback when all
* windows have redrawn.
*
- * @param callback reporting callback to be called when all windows have redrawn.
+ * @param message The message will be sent when all windows have redrawn. Note that the message
+ * must be obtained from handler, otherwise it will throw NPE.
* @param timeout calls the callback anyway after the timeout.
* @param displayId waits for the windows on the given display, INVALID_DISPLAY to wait for all
* windows on all displays.
*/
- public abstract void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId);
+ public abstract void waitForAllWindowsDrawn(Message message, long timeout, int displayId);
/**
* Overrides the display size.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6f4f075..8dc2840 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -601,7 +601,7 @@
* The callbacks to make when the windows all have been drawn for a given
* {@link WindowContainer}.
*/
- final HashMap<WindowContainer, Runnable> mWaitingForDrawnCallbacks = new HashMap<>();
+ final ArrayMap<WindowContainer<?>, Message> mWaitingForDrawnCallbacks = new ArrayMap<>();
/** List of window currently causing non-system overlay windows to be hidden. */
private ArrayList<WindowState> mHidingNonSystemOverlayWindows = new ArrayList<>();
@@ -763,8 +763,6 @@
Settings.Secure.getUriFor(Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS);
private final Uri mPolicyControlUri =
Settings.Global.getUriFor(Settings.Global.POLICY_CONTROL);
- private final Uri mPointerLocationUri =
- Settings.System.getUriFor(Settings.System.POINTER_LOCATION);
private final Uri mForceDesktopModeOnExternalDisplaysUri = Settings.Global.getUriFor(
Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS);
private final Uri mFreeformWindowUri = Settings.Global.getUriFor(
@@ -792,7 +790,6 @@
resolver.registerContentObserver(mImmersiveModeConfirmationsUri, false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(mPolicyControlUri, false, this, UserHandle.USER_ALL);
- resolver.registerContentObserver(mPointerLocationUri, false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(mForceDesktopModeOnExternalDisplaysUri, false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(mFreeformWindowUri, false, this, UserHandle.USER_ALL);
@@ -816,11 +813,6 @@
return;
}
- if (mPointerLocationUri.equals(uri)) {
- updatePointerLocation();
- return;
- }
-
if (mForceDesktopModeOnExternalDisplaysUri.equals(uri)) {
updateForceDesktopModeOnExternalDisplays();
return;
@@ -869,7 +861,6 @@
void loadSettings() {
updateSystemUiSettings(false /* handleChange */);
- updatePointerLocation();
updateMaximumObscuringOpacityForTouch();
}
@@ -900,21 +891,6 @@
}
}
- void updatePointerLocation() {
- ContentResolver resolver = mContext.getContentResolver();
- final boolean enablePointerLocation = Settings.System.getIntForUser(resolver,
- Settings.System.POINTER_LOCATION, 0, UserHandle.USER_CURRENT) != 0;
-
- if (mPointerLocationEnabled == enablePointerLocation) {
- return;
- }
- mPointerLocationEnabled = enablePointerLocation;
- synchronized (mGlobalLock) {
- mRoot.forAllDisplayPolicies(
- p -> p.setPointerLocationEnabled(mPointerLocationEnabled));
- }
- }
-
void updateForceDesktopModeOnExternalDisplays() {
ContentResolver resolver = mContext.getContentResolver();
final boolean enableForceDesktopMode = Settings.Global.getInt(resolver,
@@ -5359,8 +5335,6 @@
public static final int CLIENT_FREEZE_TIMEOUT = 30;
public static final int NOTIFY_ACTIVITY_DRAWN = 32;
- public static final int ALL_WINDOWS_DRAWN = 33;
-
public static final int NEW_ANIMATOR_SCALE = 34;
public static final int SHOW_EMULATOR_DISPLAY_OVERLAY = 36;
@@ -5482,7 +5456,7 @@
}
case WAITING_FOR_DRAWN_TIMEOUT: {
- Runnable callback = null;
+ final Message callback;
final WindowContainer<?> container = (WindowContainer<?>) msg.obj;
synchronized (mGlobalLock) {
ProtoLog.w(WM_ERROR, "Timeout waiting for drawn: undrawn=%s",
@@ -5496,7 +5470,7 @@
callback = mWaitingForDrawnCallbacks.remove(container);
}
if (callback != null) {
- callback.run();
+ callback.sendToTarget();
}
break;
}
@@ -5520,17 +5494,6 @@
}
break;
}
- case ALL_WINDOWS_DRAWN: {
- Runnable callback;
- final WindowContainer container = (WindowContainer) msg.obj;
- synchronized (mGlobalLock) {
- callback = mWaitingForDrawnCallbacks.remove(container);
- }
- if (callback != null) {
- callback.run();
- }
- break;
- }
case NEW_ANIMATOR_SCALE: {
float scale = getCurrentAnimatorScale();
ValueAnimator.setDurationScale(scale);
@@ -6078,7 +6041,8 @@
if (mWaitingForDrawnCallbacks.isEmpty()) {
return;
}
- mWaitingForDrawnCallbacks.forEach((container, callback) -> {
+ for (int i = mWaitingForDrawnCallbacks.size() - 1; i >= 0; i--) {
+ final WindowContainer<?> container = mWaitingForDrawnCallbacks.keyAt(i);
for (int j = container.mWaitingForDrawn.size() - 1; j >= 0; j--) {
final WindowState win = (WindowState) container.mWaitingForDrawn.get(j);
ProtoLog.i(WM_DEBUG_SCREEN_ON,
@@ -6104,9 +6068,9 @@
if (container.mWaitingForDrawn.isEmpty()) {
ProtoLog.d(WM_DEBUG_SCREEN_ON, "All windows drawn!");
mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
- mH.sendMessage(mH.obtainMessage(H.ALL_WINDOWS_DRAWN, container));
+ mWaitingForDrawnCallbacks.removeAt(i).sendToTarget();
}
- });
+ }
}
private void traceStartWaitingForWindowDrawn(WindowState window) {
@@ -7818,13 +7782,14 @@
}
@Override
- public void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId) {
+ public void waitForAllWindowsDrawn(Message message, long timeout, int displayId) {
+ Objects.requireNonNull(message.getTarget());
final WindowContainer<?> container = displayId == INVALID_DISPLAY
? mRoot : mRoot.getDisplayContent(displayId);
if (container == null) {
// The waiting container doesn't exist, no need to wait to run the callback. Run and
// return;
- callback.run();
+ message.sendToTarget();
return;
}
boolean allWindowsDrawn = false;
@@ -7841,13 +7806,13 @@
}
}
- mWaitingForDrawnCallbacks.put(container, callback);
+ mWaitingForDrawnCallbacks.put(container, message);
mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout);
checkDrawnWindowsLocked();
}
}
if (allWindowsDrawn) {
- callback.run();
+ message.sendToTarget();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cf1e51f..9f0c78f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2470,6 +2470,11 @@
if (allowExitAnimation && mWinAnimator.applyAnimationLocked(transit, false)) {
ProtoLog.v(WM_DEBUG_ANIM,
"Set animatingExit: reason=remove/applyAnimation win=%s", this);
+ if (startingWindow && mSurfaceAnimator.hasLeash()) {
+ // Keep starting window on top during fade-out animation.
+ getPendingTransaction().setLayer(mSurfaceAnimator.mLeash,
+ Integer.MAX_VALUE);
+ }
mAnimatingExit = true;
// mAnimatingExit affects canAffectSystemUiFlags(). Run layout such that
@@ -5665,7 +5670,7 @@
}
if (mIsWallpaper) {
// TODO(b/233286785): Add sync support to wallpaper.
- return false;
+ return true;
}
// In the WindowContainer implementation we immediately mark ready
// since a generic WindowContainer only needs to wait for its
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 31afcbf..9806be8 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -388,12 +388,23 @@
@Override
SurfaceControl.Builder makeSurface() {
final SurfaceControl.Builder builder = super.makeSurface();
+ // The overlay may use COLOR_MODE_A8 that needs to be at the top of the display to avoid
+ // additional memory usage, see b/235601833. Note that getParentSurfaceControl() must use
+ // the same parent.
if (mRoundedCornerOverlay) {
builder.setParent(null);
}
return builder;
}
+ @Override
+ public SurfaceControl getParentSurfaceControl() {
+ if (mRoundedCornerOverlay) {
+ return null;
+ }
+ return super.getParentSurfaceControl();
+ }
+
boolean isClientVisible() {
return mClientVisible;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4305416..2f1d67d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -19507,8 +19507,9 @@
public boolean isCurrentInputMethodSetByOwner() {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
- || isProfileOwner(caller) || isSystemUid(caller),
- "Only profile owner, device owner and system may call this method.");
+ || isProfileOwner(caller) || canQueryAdminPolicy(caller) || isSystemUid(caller),
+ "Only profile owner, device owner, a caller with QUERY_ADMIN_POLICY "
+ + "permission or system may call this method.");
return getUserData(caller.getUserId()).mCurrentInputMethodSet;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ec3e734..2f2e1cb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1559,9 +1559,11 @@
mSystemServiceManager.startService(ROLE_SERVICE_CLASS);
t.traceEnd();
- t.traceBegin("StartVibratorManagerService");
- mSystemServiceManager.startService(VibratorManagerService.Lifecycle.class);
- t.traceEnd();
+ if (!isTv) {
+ t.traceBegin("StartVibratorManagerService");
+ mSystemServiceManager.startService(VibratorManagerService.Lifecycle.class);
+ t.traceEnd();
+ }
t.traceBegin("StartDynamicSystemService");
dynamicSystem = new DynamicSystemService(context);
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
index e33ca77..b7a0cf3 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
+++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
@@ -138,6 +138,14 @@
}
@Test
+ public void testGetUserMinAspectRatio_withCrossUserId() {
+ final int crossUserId = UserHandle.myUserId() + 1;
+ assertThrows(SecurityException.class,
+ () -> mIPackageManager.getUserMinAspectRatio(
+ mInstrumentation.getContext().getPackageName(), crossUserId));
+ }
+
+ @Test
public void testIsPackageSignedByKeySet_cannotDetectCrossUserPkg() throws Exception {
final KeySet keySet = mIPackageManager.getSigningKeySet(mContext.getPackageName());
assertThrows(IllegalArgumentException.class,
diff --git a/services/tests/PackageManagerServiceTests/server/res/raw/install_app2_cert5_rotated_cert6 b/services/tests/PackageManagerServiceTests/server/res/raw/install_app2_cert5_rotated_cert6
index 30bb647..6feebb8 100644
--- a/services/tests/PackageManagerServiceTests/server/res/raw/install_app2_cert5_rotated_cert6
+++ b/services/tests/PackageManagerServiceTests/server/res/raw/install_app2_cert5_rotated_cert6
Binary files differ
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
index 435f0d7..a805e5c 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -20,6 +20,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static java.lang.reflect.Modifier.isFinal;
@@ -138,7 +139,7 @@
.setSecondaryCpuAbiString("secondaryCpuAbiString")
.setCpuAbiOverrideString("cpuAbiOverrideString")
.build();
- pri.populateUsers(new int[] {
+ pri.populateUsers(new int[]{
1, 2, 3, 4, 5
}, setting);
Assert.assertNotNull(pri.mBroadcastUsers);
@@ -150,7 +151,7 @@
pri.mBroadcastUsers = null;
final int EXCLUDED_USER_ID = 4;
setting.setInstantApp(true, EXCLUDED_USER_ID);
- pri.populateUsers(new int[] {
+ pri.populateUsers(new int[]{
1, 2, 3, EXCLUDED_USER_ID, 5
}, setting);
Assert.assertNotNull(pri.mBroadcastUsers);
@@ -164,8 +165,8 @@
@Test
public void testPartitions() {
- String[] partitions = { "system", "vendor", "odm", "oem", "product", "system_ext" };
- String[] appdir = { "app", "priv-app" };
+ String[] partitions = {"system", "vendor", "odm", "oem", "product", "system_ext"};
+ String[] appdir = {"app", "priv-app"};
for (int i = 0; i < partitions.length; i++) {
final ScanPartition scanPartition =
PackageManagerService.SYSTEM_PARTITIONS.get(i);
@@ -425,10 +426,10 @@
private String displayName(Method m) {
String r = m.getName();
String p = Arrays.toString(m.getGenericParameterTypes())
- .replaceAll("([a-zA-Z0-9]+\\.)+", "")
- .replace("class ", "")
- .replaceAll("^\\[", "(")
- .replaceAll("\\]$", ")");
+ .replaceAll("([a-zA-Z0-9]+\\.)+", "")
+ .replace("class ", "")
+ .replaceAll("^\\[", "(")
+ .replaceAll("\\]$", ")");
return r + p;
}
@@ -612,4 +613,22 @@
runShellCommand("pm uninstall " + TEST_PKG_NAME);
}
}
+
+ @Test
+ public void testSetUserMinAspectRatio_samePackage_succeeds() throws Exception {
+ mIPackageManager.setUserMinAspectRatio(PACKAGE_NAME, UserHandle.myUserId(),
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
+ // Invoking setUserMinAspectRatio on the same package shouldn't get any exception.
+ }
+
+ @Test
+ public void testSetUserMinAspectRatio_differentPackage_fails() {
+ final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
+ runShellCommand("pm install " + testApk);
+ assertThrows(SecurityException.class, () -> {
+ mIPackageManager.setUserMinAspectRatio(TEST_PKG_NAME, UserHandle.myUserId(),
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
+ });
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ }
}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index 836f858..16fb012 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -971,7 +971,7 @@
origPkgSetting01.setUserState(0, 100, 1, true, false, false, false, 0, null, false,
false, "lastDisabledCaller", new ArraySet<>(new String[]{"enabledComponent1"}),
new ArraySet<>(new String[]{"disabledComponent1"}), 0, 0, "harmfulAppWarning",
- "splashScreenTheme", 1000L);
+ "splashScreenTheme", 1000L, PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
final PersistableBundle appExtras1 = createPersistableBundle(
PACKAGE_NAME_1, 1L, 0.01, true, "appString1");
final PersistableBundle launcherExtras1 = createPersistableBundle(
@@ -1638,7 +1638,8 @@
: oldUserState.getSharedLibraryOverlayPaths() == null)
&& userState.getSplashScreenTheme().equals(
oldUserState.getSplashScreenTheme())
- && userState.getUninstallReason() == oldUserState.getUninstallReason();
+ && userState.getUninstallReason() == oldUserState.getUninstallReason()
+ && userState.getMinAspectRatio() == oldUserState.getMinAspectRatio();
}
private SharedUserSetting createSharedUserSetting(Settings settings, String userName,
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 434a75f..b5f03ba 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -1086,6 +1086,16 @@
.getThermalBrightnessThrottlingDataMapByThrottlingId();
}
+ @Test
+ public void testSetBrightness_BrightnessShouldBeClamped() {
+ float clampedBrightness = 0.6f;
+ when(mHolder.hbmController.getCurrentBrightnessMax()).thenReturn(clampedBrightness);
+
+ mHolder.dpc.setBrightness(PowerManager.BRIGHTNESS_MAX);
+
+ verify(mHolder.brightnessSetting).setBrightness(clampedBrightness);
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index db786bd..f3e3d34 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1092,6 +1092,16 @@
.getThermalBrightnessThrottlingDataMapByThrottlingId();
}
+ @Test
+ public void testSetBrightness_BrightnessShouldBeClamped() {
+ float clampedBrightness = 0.6f;
+ when(mHolder.hbmController.getCurrentBrightnessMax()).thenReturn(clampedBrightness);
+
+ mHolder.dpc.setBrightness(PowerManager.BRIGHTNESS_MAX);
+
+ verify(mHolder.brightnessSetting).setBrightness(clampedBrightness);
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index f89f73c9..aa0a2fe 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -1257,6 +1257,17 @@
public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
return mSurfaceControlProxy;
}
+
+ // Instead of using DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay)
+ // we should use DisplayDeviceConfig.create(context, isFirstDisplay) for the test to ensure
+ // that real device DisplayDeviceConfig is not loaded for FakeDisplay and we are getting
+ // consistent behaviour. Please also note that context passed to this method, is
+ // mMockContext and values will be loaded from mMockResources.
+ @Override
+ public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
+ long physicalDisplayId, boolean isFirstDisplay) {
+ return DisplayDeviceConfig.create(context, isFirstDisplay);
+ }
}
private class TestListener implements DisplayAdapter.Listener {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
index 738b927..9698552 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
@@ -24,15 +24,19 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IRemoteCallback;
+import android.os.Looper;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -48,11 +52,18 @@
private static final String FAKE_PACKAGE_NAME = "com.android.server.pm.fakeapp";
private static final int FAKE_PACKAGE_UID = 123;
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
PackageMonitorCallbackHelper mPackageMonitorCallbackHelper;
+ @Rule
+ public final MockSystemRule mMockSystem = new MockSystemRule();
+
@Before
public void setup() {
- mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper();
+ when(mMockSystem.mocks().getInjector().getHandler()).thenReturn(mHandler);
+ mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper(
+ mMockSystem.mocks().getInjector());
}
@@ -67,6 +78,7 @@
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */);
+ Thread.sleep(300);
verify(callback, never()).sendResult(any());
}
@@ -78,6 +90,7 @@
mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */);
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0});
+ Thread.sleep(300);
verify(callback, times(1)).sendResult(any());
@@ -85,6 +98,7 @@
mPackageMonitorCallbackHelper.unregisterPackageMonitorCallback(callback);
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */);
+ Thread.sleep(300);
verify(callback, never()).sendResult(any());
}
@@ -96,6 +110,7 @@
mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */);
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */);
+ Thread.sleep(300);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(callback, times(1)).sendResult(bundleCaptor.capture());
@@ -117,6 +132,7 @@
// Notify for user 10
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */);
+ Thread.sleep(300);
verify(callback, never()).sendResult(any());
}
@@ -132,6 +148,7 @@
mPackageMonitorCallbackHelper.notifyPackageChanged(FAKE_PACKAGE_NAME,
false /* dontKillApp */, components, FAKE_PACKAGE_UID, null /* reason */,
new int[]{0} /* userIds */);
+ Thread.sleep(300);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(callback, times(1)).sendResult(bundleCaptor.capture());
@@ -158,6 +175,7 @@
mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(FAKE_PACKAGE_NAME,
FAKE_PACKAGE_UID, new int[]{0} /* userIds */, new int[0],
PackageInstaller.DATA_LOADER_TYPE_STREAMING);
+ Thread.sleep(300);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(callback, times(1)).sendResult(bundleCaptor.capture());
@@ -180,6 +198,7 @@
mPackageMonitorCallbackHelper.notifyResourcesChanged(true /* mediaStatus */,
true /* replacing */, new String[]{FAKE_PACKAGE_NAME},
new int[]{FAKE_PACKAGE_UID} /* uids */);
+ Thread.sleep(300);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(callback, times(1)).sendResult(bundleCaptor.capture());
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 7cd8819..0664ab8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -15,12 +15,18 @@
*/
package com.android.server.pm;
+import static android.os.UserManager.DISALLOW_USER_SWITCH;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -30,6 +36,7 @@
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -88,6 +95,7 @@
public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
.spyStatic(UserManager.class)
.spyStatic(LocalServices.class)
+ .spyStatic(SystemProperties.class)
.mockStatic(Settings.Global.class)
.build();
@@ -325,6 +333,192 @@
() -> mUmi.getBootUser(/* waitUntilSet= */ false));
}
+ @Test
+ public void testGetPreviousFullUserToEnterForeground() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ assertWithMessage("getPreviousFullUserToEnterForeground")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(OTHER_USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsCurrentUser() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mockCurrentUser(OTHER_USER_ID);
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip current user")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsNonFullUsers() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mUsers.get(OTHER_USER_ID).info.flags &= ~UserInfo.FLAG_FULL;
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip non-full users")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsPartialUsers() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mUsers.get(OTHER_USER_ID).info.partial = true;
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip partial users")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsDisabledUsers() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mUsers.get(OTHER_USER_ID).info.flags |= UserInfo.FLAG_DISABLED;
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip disabled users")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsRemovingUsers() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mUms.addRemovingUserId(OTHER_USER_ID);
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip removing users")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void assertIsUserSwitcherEnabledOnMultiUserSettings() throws Exception {
+ resetUserSwitcherEnabled();
+
+ mockUserSwitcherEnabled(false);
+ assertThat(mUms.isUserSwitcherEnabled(USER_ID)).isFalse();
+
+ mockUserSwitcherEnabled(true);
+ assertThat(mUms.isUserSwitcherEnabled(USER_ID)).isTrue();
+ }
+
+ @Test
+ public void assertIsUserSwitcherEnabledOnMaxSupportedUsers() throws Exception {
+ resetUserSwitcherEnabled();
+
+ mockMaxSupportedUsers(/* maxUsers= */ 1);
+ assertThat(UserManager.supportsMultipleUsers()).isFalse();
+ assertThat(mUms.isUserSwitcherEnabled(USER_ID)).isFalse();
+
+ mockMaxSupportedUsers(/* maxUsers= */ 8);
+ assertThat(UserManager.supportsMultipleUsers()).isTrue();
+ assertThat(mUms.isUserSwitcherEnabled(USER_ID)).isTrue();
+ }
+
+ @Test
+ public void assertIsUserSwitcherEnabled() throws Exception {
+ resetUserSwitcherEnabled();
+
+ mockMaxSupportedUsers(/* maxUsers= */ 8);
+ assertThat(mUms.isUserSwitcherEnabled(true, USER_ID)).isTrue();
+
+ mockUserSwitcherEnabled(false);
+ assertThat(mUms.isUserSwitcherEnabled(true, USER_ID)).isFalse();
+
+ mockUserSwitcherEnabled(true);
+ assertThat(mUms.isUserSwitcherEnabled(false, USER_ID)).isTrue();
+
+ mUms.setUserRestriction(DISALLOW_USER_SWITCH, true, USER_ID);
+ assertThat(mUms.isUserSwitcherEnabled(false, USER_ID)).isFalse();
+
+ mUms.setUserRestriction(DISALLOW_USER_SWITCH, false, USER_ID);
+ mockMaxSupportedUsers(1);
+ assertThat(mUms.isUserSwitcherEnabled(true, USER_ID)).isFalse();
+ }
+
+ @Test
+ public void assertIsUserSwitcherEnabledOnShowMultiuserUI() throws Exception {
+ resetUserSwitcherEnabled();
+
+ mockShowMultiuserUI(/* show= */ false);
+ assertThat(UserManager.supportsMultipleUsers()).isFalse();
+ assertThat(mUms.isUserSwitcherEnabled(USER_ID)).isFalse();
+
+ mockShowMultiuserUI(/* show= */ true);
+ assertThat(UserManager.supportsMultipleUsers()).isTrue();
+ assertThat(mUms.isUserSwitcherEnabled(USER_ID)).isTrue();
+ }
+
+ @Test
+ public void assertIsUserSwitcherEnabledOnUserRestrictions() throws Exception {
+ resetUserSwitcherEnabled();
+
+ mUms.setUserRestriction(DISALLOW_USER_SWITCH, true, USER_ID);
+ assertThat(mUms.isUserSwitcherEnabled(USER_ID)).isFalse();
+
+ mUms.setUserRestriction(DISALLOW_USER_SWITCH, false, USER_ID);
+ assertThat(mUms.isUserSwitcherEnabled(USER_ID)).isTrue();
+ }
+
+ @Test
+ public void assertIsUserSwitcherEnabledOnDemoMode() throws Exception {
+ resetUserSwitcherEnabled();
+
+ mockDeviceDemoMode(/* enabled= */ true);
+ assertThat(mUms.isUserSwitcherEnabled(USER_ID)).isFalse();
+
+ mockDeviceDemoMode(/* enabled= */ false);
+ assertThat(mUms.isUserSwitcherEnabled(USER_ID)).isTrue();
+ }
+
+ private void resetUserSwitcherEnabled() {
+ mUms.putUserInfo(new UserInfo(USER_ID, "Test User", 0));
+ mUms.setUserRestriction(DISALLOW_USER_SWITCH, false, USER_ID);
+ mockUserSwitcherEnabled(/* enabled= */ true);
+ mockDeviceDemoMode(/* enabled= */ false);
+ mockMaxSupportedUsers(/* maxUsers= */ 8);
+ mockShowMultiuserUI(/* show= */ true);
+ }
+
+ private void mockUserSwitcherEnabled(boolean enabled) {
+ doReturn(enabled ? 1 : 0).when(() -> Settings.Global.getInt(
+ any(), eq(android.provider.Settings.Global.USER_SWITCHER_ENABLED), anyInt()));
+ }
+
+ private void mockDeviceDemoMode(boolean enabled) {
+ doReturn(enabled ? 1 : 0).when(() -> Settings.Global.getInt(
+ any(), eq(android.provider.Settings.Global.DEVICE_DEMO_MODE), anyInt()));
+ }
+
+ private void mockMaxSupportedUsers(int maxUsers) {
+ doReturn(maxUsers).when(() ->
+ SystemProperties.getInt(eq("fw.max_users"), anyInt()));
+ }
+
+ private void mockShowMultiuserUI(boolean show) {
+ doReturn(show).when(() ->
+ SystemProperties.getBoolean(eq("fw.show_multiuserui"), anyBoolean()));
+ }
+
private void mockCurrentUser(@UserIdInt int userId) {
mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal);
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index dccacb4..24a628e 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -1342,6 +1342,10 @@
Message copy = new Message();
copy.copyFrom(msg);
mMessages.add(copy);
+ if (msg.getCallback() != null) {
+ msg.getCallback().run();
+ msg.setCallback(null);
+ }
return super.sendMessageAtTime(msg, uptimeMillis);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
index bc09d4b..399655ffa 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
@@ -137,6 +137,20 @@
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
+
+ /**
+ * Override displayOsd to prevent it from broadcasting an intent, which
+ * can trigger a SecurityException.
+ */
+ @Override
+ void displayOsd(int messageId) {
+ // do nothing
+ }
+
+ @Override
+ void displayOsd(int messageId, int extra) {
+ // do nothing
+ }
};
mLooper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 3bde665..cb19029 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -61,6 +61,7 @@
private HdmiPortInfo[] mHdmiPortInfo = null;
private HdmiCecController.HdmiCecCallback mCallback = null;
private int mCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
+ private boolean mIsCecControlEnabled = true;
@Override
public String nativeInit() {
@@ -128,7 +129,9 @@
public void enableCec(boolean enabled) {}
@Override
- public void enableSystemCecControl(boolean enabled) {}
+ public void enableSystemCecControl(boolean enabled) {
+ mIsCecControlEnabled = enabled;
+ }
@Override
public void nativeSetLanguage(String language) {}
@@ -154,6 +157,10 @@
mPortConnectionStatus.put(port, connected);
}
+ public boolean getIsCecControlEnabled() {
+ return mIsCecControlEnabled;
+ }
+
public void setCecVersion(@HdmiControlManager.HdmiCecVersion int cecVersion) {
mCecVersion = cecVersion;
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java
index 7c8a11e..04f921f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java
@@ -24,10 +24,18 @@
*/
final class FakePowerManagerWrapper extends PowerManagerWrapper {
private boolean mInteractive;
+ private WakeLockWrapper mWakeLock;
+ private boolean mWasWakeLockInstanceCreated = false;
+
FakePowerManagerWrapper(@NonNull Context context) {
+ this(context, null);
+ }
+
+ FakePowerManagerWrapper(@NonNull Context context, WakeLockWrapper wakeLock) {
super(context);
mInteractive = true;
+ mWakeLock = wakeLock;
}
@Override
@@ -51,5 +59,48 @@
return;
}
- // Don't stub WakeLock.
+ @Override
+ WakeLockWrapper newWakeLock(int levelAndFlags, String tag) {
+ if (mWakeLock == null) {
+ mWakeLock = new FakeWakeLockWrapper();
+ }
+ mWasWakeLockInstanceCreated = true;
+ return mWakeLock;
+ }
+
+ boolean wasWakeLockInstanceCreated() {
+ return mWasWakeLockInstanceCreated;
+ }
+
+ /**
+ * "Fake" wrapper for {@link PowerManager.WakeLock}, as opposed to a "Default" wrapper used by
+ * the framework - see {@link PowerManagerWrapper.DefaultWakeLockWrapper}.
+ */
+ public static class FakeWakeLockWrapper implements WakeLockWrapper {
+ private static final String TAG = "FakeWakeLockWrapper";
+ private boolean mWakeLockHeld = false;
+
+ @Override
+ public void acquire(long timeout) {
+ mWakeLockHeld = true;
+ }
+
+ @Override
+ public void acquire() {
+ mWakeLockHeld = true;
+ }
+
+ @Override
+ public void release() {
+ mWakeLockHeld = false;
+ }
+
+ @Override
+ public boolean isHeld() {
+ return mWakeLockHeld;
+ }
+
+ @Override
+ public void setReferenceCounted(boolean value) {}
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index e820a9e..0d172fdb 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -21,6 +21,7 @@
import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
+import static com.android.server.hdmi.HdmiControlService.DEVICE_CLEANUP_TIMEOUT;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
@@ -37,6 +38,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -63,6 +65,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.InOrder;
import org.mockito.Mockito;
import java.util.ArrayList;
@@ -87,6 +90,7 @@
private HdmiEarcController mHdmiEarcController;
private FakeEarcNativeWrapper mEarcNativeWrapper;
private FakePowerManagerWrapper mPowerManager;
+ private WakeLockWrapper mWakeLockSpy;
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
@@ -164,7 +168,8 @@
.build();
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiControlServiceSpy.initService();
- mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+ mWakeLockSpy = spy(new FakePowerManagerWrapper.FakeWakeLockWrapper());
+ mPowerManager = new FakePowerManagerWrapper(mContextSpy, mWakeLockSpy);
mHdmiControlServiceSpy.setPowerManager(mPowerManager);
mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mHdmiControlServiceSpy.setEarcSupported(true);
@@ -190,6 +195,7 @@
doReturn(true).when(mHdmiControlServiceSpy).isStandbyMessageReceived();
mHdmiControlServiceSpy.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+
assertTrue(mPlaybackDeviceSpy.isStandby());
assertTrue(mAudioSystemDeviceSpy.isStandby());
assertTrue(mPlaybackDeviceSpy.isDisabled());
@@ -197,6 +203,75 @@
}
@Test
+ public void playbackOnlyDevice_onStandbyCompleted_disableCecController() {
+ mLocalDevices.remove(mAudioSystemDeviceSpy);
+ mHdmiControlServiceSpy.clearCecLocalDevices();
+ mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
+
+ assertTrue(mNativeWrapper.getIsCecControlEnabled());
+ mHdmiControlServiceSpy.disableCecLocalDevices(
+ new HdmiCecLocalDevice.PendingActionClearedCallback() {
+ @Override
+ public void onCleared(HdmiCecLocalDevice device) {
+ assertTrue(mNativeWrapper.getIsCecControlEnabled());
+ mHdmiControlServiceSpy.onPendingActionsCleared(
+ HdmiControlService.STANDBY_SCREEN_OFF);
+ }
+ });
+ mTestLooper.dispatchAll();
+
+ verify(mPlaybackDeviceSpy, times(1)).invokeStandbyCompletedCallback(any());
+ assertFalse(mNativeWrapper.getIsCecControlEnabled());
+ }
+
+
+ @Test
+ public void playbackAndAudioDevice_onStandbyCompleted_doNotDisableCecController() {
+ mLocalDeviceTypes.add(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
+
+ assertTrue(mNativeWrapper.getIsCecControlEnabled());
+ mHdmiControlServiceSpy.disableCecLocalDevices(
+ new HdmiCecLocalDevice.PendingActionClearedCallback() {
+ @Override
+ public void onCleared(HdmiCecLocalDevice device) {
+ assertTrue(mNativeWrapper.getIsCecControlEnabled());
+ mHdmiControlServiceSpy.onPendingActionsCleared(
+ HdmiControlService.STANDBY_SCREEN_OFF);
+ }
+ });
+ mTestLooper.dispatchAll();
+
+ verify(mPlaybackDeviceSpy, times(1)).invokeStandbyCompletedCallback(any());
+ verify(mAudioSystemDeviceSpy, times(1)).invokeStandbyCompletedCallback(any());
+ assertTrue(mNativeWrapper.getIsCecControlEnabled());
+ }
+
+ @Test
+ public void onStandby_acquireAndReleaseWakeLockSuccessfully() {
+ mHdmiControlServiceSpy.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+ doReturn(true).when(mHdmiControlServiceSpy).isStandbyMessageReceived();
+ mTestLooper.dispatchAll();
+
+ assertFalse(mPowerManager.wasWakeLockInstanceCreated());
+ mHdmiControlServiceSpy.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+
+ InOrder inOrder = inOrder(mHdmiControlServiceSpy, mWakeLockSpy);
+ inOrder.verify(mWakeLockSpy, times(1)).acquire(DEVICE_CLEANUP_TIMEOUT);
+ inOrder.verify(mHdmiControlServiceSpy, times(1)).disableCecLocalDevices(any());
+ inOrder.verify(mHdmiControlServiceSpy, times(1))
+ .onPendingActionsCleared(HdmiControlService.STANDBY_SCREEN_OFF);
+ inOrder.verify(mWakeLockSpy, times(1)).release();
+
+ assertTrue(mPowerManager.wasWakeLockInstanceCreated());
+ }
+
+ @Test
public void initialPowerStatus_normalBoot_isTransientToStandby() {
assertThat(mHdmiControlServiceSpy.getInitialPowerStatus()).isEqualTo(
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
@@ -1450,8 +1525,10 @@
}
@Override
- protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ protected void onStandby(boolean initiatedByCec, int standbyAction,
+ StandbyCompletedCallback callback) {
mIsStandby = true;
+ invokeStandbyCompletedCallback(callback);
}
protected boolean isStandby() {
@@ -1503,8 +1580,10 @@
}
@Override
- protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ protected void onStandby(boolean initiatedByCec, int standbyAction,
+ StandbyCompletedCallback callback) {
mIsStandby = true;
+ invokeStandbyCompletedCallback(callback);
}
protected boolean isStandby() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java
index 079ef2e..024e36d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java
@@ -262,4 +262,63 @@
assertThat(mNativeWrapper.getResultMessages()).isEmpty();
}
+
+ @Test
+ public void adjustOnlyAvbEnabled_savlBecomesSupported_switchToAvb() {
+ enableAdjustOnlyAbsoluteVolumeBehavior();
+ mNativeWrapper.clearResultMessages();
+
+ // When the Audio System reports support for <Set Audio Volume Level>,
+ // the device should start the process for adopting AVB by sending <Give Audio Status>
+ receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
+ verifyGiveAudioStatusSent();
+
+ // The device should use adjust-only AVB while waiting for <Report Audio Status>
+ assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+
+ // The device should switch to AVB upon receiving <Report Audio Status>
+ receiveReportAudioStatus(60, false);
+ assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+
+ }
+
+ /**
+ * Tests that adjust-only AVB is not interrupted when a device's support for
+ * <Set Audio Volume Level> becomes unknown.
+ *
+ * This may currently occur when NewDeviceAction overwrites a device's info in HdmiCecNetwork.
+ * However, because replicating this scenario would be brittle and the behavior may change,
+ * this test does not simulate it and instead changes HdmiCecNetwork directly.
+ */
+ @Test
+ public void adjustOnlyAvbEnabled_savlSupportBecomesUnknown_keepUsingAdjustOnlyAvb() {
+ enableAdjustOnlyAbsoluteVolumeBehavior();
+ mNativeWrapper.clearResultMessages();
+
+ // Make sure the existing SetAudioVolumeLevelDiscoveryAction expires,
+ // so that we can check whether a new one is started.
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS + 1);
+ mTestLooper.dispatchAll();
+
+ // Replace Audio System device info with one that has unknown support for all features
+ HdmiDeviceInfo updatedAudioSystemDeviceInfo =
+ mHdmiControlService.getHdmiCecNetwork().getDeviceInfo(Constants.ADDR_AUDIO_SYSTEM)
+ .toBuilder()
+ .setDeviceFeatures(DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN)
+ .build();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(updatedAudioSystemDeviceInfo);
+ mTestLooper.dispatchAll();
+
+ // The device should not switch away from adjust-only AVB
+ assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+
+ // The device should query support for <Set Audio Volume Level> again
+ assertThat(mNativeWrapper.getResultMessages()).contains(
+ SetAudioVolumeLevelMessage.build(
+ getLogicalAddress(), getSystemAudioDeviceLogicalAddress(),
+ Constants.AUDIO_VOLUME_STATUS_UNKNOWN));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index 77bdf19..3a3ab84 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -18,6 +18,8 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNot.not;
+import static org.hamcrest.core.StringContains.containsString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@@ -53,7 +55,7 @@
@Test
public void getSeInfoOptInToLatest() {
- var packageState = makePackageState(Build.VERSION_CODES.P);
+ var packageState = new PackageStateBuilder(Build.VERSION_CODES.P).build();
when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
.thenReturn(true);
@@ -64,7 +66,7 @@
@Test
public void getSeInfoOptInToR() {
- var packageState = makePackageState(Build.VERSION_CODES.P);
+ var packageState = new PackageStateBuilder(Build.VERSION_CODES.P).build();
when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
.thenReturn(true);
@@ -75,7 +77,7 @@
@Test
public void getSeInfoNoOptIn() {
- var packageState = makePackageState(Build.VERSION_CODES.P);
+ var packageState = new PackageStateBuilder(Build.VERSION_CODES.P).build();
when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
.thenReturn(false);
@@ -86,7 +88,7 @@
@Test
public void getSeInfoNoOptInButAlreadyLatest() {
- var packageState = makePackageState(LATEST_OPT_IN_VERSION);
+ var packageState = new PackageStateBuilder(LATEST_OPT_IN_VERSION).build();
when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
.thenReturn(false);
@@ -97,7 +99,7 @@
@Test
public void getSeInfoTargetingCurDevelopment() {
- var packageState = makePackageState(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ var packageState = new PackageStateBuilder(Build.VERSION_CODES.CUR_DEVELOPMENT).build();
when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
.thenReturn(true);
@@ -108,7 +110,7 @@
@Test
public void getSeInfoNoOptInButAlreadyR() {
- var packageState = makePackageState(R_OPT_IN_VERSION);
+ var packageState = new PackageStateBuilder(R_OPT_IN_VERSION).build();
when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
.thenReturn(false);
@@ -119,7 +121,7 @@
@Test
public void getSeInfoOptInRButLater() {
- var packageState = makePackageState(R_OPT_IN_VERSION + 1);
+ var packageState = new PackageStateBuilder(R_OPT_IN_VERSION + 1).build();
when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
.thenReturn(true);
@@ -128,15 +130,114 @@
is("default:targetSdkVersion=" + (R_OPT_IN_VERSION + 1)));
}
- private PackageState makePackageState(int targetSdkVersion) {
- var packageState = Mockito.mock(PackageState.class);
- when(packageState.getPackageName()).thenReturn(PACKAGE_NAME);
- when(packageState.getAndroidPackage()).thenReturn(
- ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
- .setTargetSdkVersion(targetSdkVersion)
- .hideAsParsed())
- .hideAsFinal()
- );
- return packageState;
+ @Test
+ public void getSeInfoPreinstalledToSystem() {
+ var packageState = new PackageStateBuilder(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .setSystem(true).build();
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
+ argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
+ .thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(packageState, packageState.getAndroidPackage(), null,
+ mMockCompatibility),
+ containsString(":partition=system"));
+ }
+
+
+ @Test
+ public void getSeInfoPreinstalledToSystemExt() {
+ var packageState = new PackageStateBuilder(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .setSystem(true).setSystemExt(true).build();
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
+ argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
+ .thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(packageState, packageState.getAndroidPackage(), null,
+ mMockCompatibility),
+ containsString(":partition=system_ext"));
+ }
+
+
+ @Test
+ public void getSeInfoPreinstalledToProduct() {
+ var packageState = new PackageStateBuilder(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .setSystem(true).setProduct(true).build();
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
+ argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
+ .thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(packageState, packageState.getAndroidPackage(), null,
+ mMockCompatibility),
+ containsString(":partition=product"));
+ }
+
+
+ @Test
+ public void getSeInfoPreinstalledToVendor() {
+ var packageState = new PackageStateBuilder(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .setSystem(true).setVendor(true).build();
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
+ argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
+ .thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(packageState, packageState.getAndroidPackage(), null,
+ mMockCompatibility),
+ containsString(":partition=vendor"));
+ }
+
+
+ @Test
+ public void getSeInfoNotPreinstalled() {
+ var packageState = new PackageStateBuilder(Build.VERSION_CODES.CUR_DEVELOPMENT).build();
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
+ argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
+ .thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(packageState, packageState.getAndroidPackage(), null,
+ mMockCompatibility),
+ not(containsString(":partition=")));
+ }
+
+ private static class PackageStateBuilder {
+ private final int mTargetSdkVersion;
+ private boolean mIsSystem = false;
+ private boolean mIsSystemExt = false;
+ private boolean mIsProduct = false;
+ private boolean mIsVendor = false;
+
+ PackageStateBuilder(int targetSdkVersion) {
+ mTargetSdkVersion = targetSdkVersion;
+ }
+
+ PackageStateBuilder setSystem(boolean isSystem) {
+ mIsSystem = isSystem;
+ return this;
+ }
+
+ PackageStateBuilder setSystemExt(boolean isSystemExt) {
+ mIsSystemExt = isSystemExt;
+ return this;
+ }
+
+ PackageStateBuilder setProduct(boolean isProduct) {
+ mIsProduct = isProduct;
+ return this;
+ }
+
+ PackageStateBuilder setVendor(boolean isVendor) {
+ mIsVendor = isVendor;
+ return this;
+ }
+
+ PackageState build() {
+ var packageState = Mockito.mock(PackageState.class);
+ when(packageState.getPackageName()).thenReturn(PACKAGE_NAME);
+ when(packageState.getAndroidPackage()).thenReturn(
+ ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(mTargetSdkVersion)
+ .hideAsParsed())
+ .hideAsFinal()
+ );
+ when(packageState.isSystem()).thenReturn(mIsSystem);
+ when(packageState.isSystemExt()).thenReturn(mIsSystemExt);
+ when(packageState.isProduct()).thenReturn(mIsProduct);
+ when(packageState.isVendor()).thenReturn(mIsVendor);
+ return packageState;
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
index fdf94be..39cc653 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
@@ -182,7 +182,7 @@
UserInfo secondaryUser = addUser();
UserInfo profile = addProfile(secondaryUser);
// Add the profile it to the users being removed.
- mUserManagerService.addRemovingUserIdLocked(profile.id);
+ mUserManagerService.addRemovingUserId(profile.id);
// We should reuse the badge from the profile being removed.
assertEquals("Badge index not reused while removing a user", 0,
mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id,
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
index 1f4c9f8..b6fd65e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
@@ -111,7 +111,7 @@
private void removeUser(int userId) {
mUserManagerService.removeUserInfo(userId);
- mUserManagerService.addRemovingUserIdLocked(userId);
+ mUserManagerService.addRemovingUserId(userId);
}
private void assertNoNextIdAvailable(String message) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 4e1196f..b4281d63c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -70,7 +70,9 @@
LocalServices.removeServiceForTest(UserManagerInternal.class);
mUserManagerService = new UserManagerService(InstrumentationRegistry.getContext());
-
+ // Put the current user to mUsers. UMS can't find userlist.xml, and fallbackToSingleUserLP.
+ mUserManagerService.putUserInfo(
+ new UserInfo(ActivityManager.getCurrentUser(), "Current User", 0));
restrictionsFile = new File(mContext.getCacheDir(), "restrictions.xml");
restrictionsFile.delete();
}
@@ -193,127 +195,8 @@
.isFalse();
}
- @Test
- public void assertIsUserSwitcherEnabledOnMultiUserSettings() throws Exception {
- int userId = ActivityManager.getCurrentUser();
- resetUserSwitcherEnabled();
-
- setUserSwitch(false);
- assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
-
- setUserSwitch(true);
- assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
- }
-
- @Test
- public void assertIsUserSwitcherEnabledOnMaxSupportedUsers() throws Exception {
- int userId = ActivityManager.getCurrentUser();
- setMaxSupportedUsers(1);
-
- assertThat(UserManager.supportsMultipleUsers()).isFalse();
- assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
-
- setMaxSupportedUsers(8);
-
- assertThat(UserManager.supportsMultipleUsers()).isTrue();
- assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
- }
-
- @Test
- public void assertIsUserSwitcherEnabled() throws Exception {
- int userId = ActivityManager.getCurrentUser();
- setMaxSupportedUsers(8);
- assertThat(mUserManagerService.isUserSwitcherEnabled(true, userId)).isTrue();
-
- setUserSwitch(false);
- assertThat(mUserManagerService.isUserSwitcherEnabled(true, userId)).isFalse();
-
- setUserSwitch(true);
- assertThat(mUserManagerService.isUserSwitcherEnabled(false, userId)).isTrue();
-
- mUserManagerService.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, userId);
- assertThat(mUserManagerService.isUserSwitcherEnabled(false, userId)).isFalse();
-
- mUserManagerService.setUserRestriction(UserManager.DISALLOW_ADD_USER, false, userId);
- setMaxSupportedUsers(1);
- assertThat(mUserManagerService.isUserSwitcherEnabled(true, userId)).isFalse();
- }
-
- @Test
- public void assertIsUserSwitcherEnabledOnShowMultiuserUI() throws Exception {
- int userId = ActivityManager.getCurrentUser();
- setShowMultiuserUI(false);
-
- assertThat(UserManager.supportsMultipleUsers()).isFalse();
- assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
-
- setShowMultiuserUI(true);
-
- assertThat(UserManager.supportsMultipleUsers()).isTrue();
- assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
- }
-
- @Test
- public void assertIsUserSwitcherEnabledOnUserRestrictions() throws Exception {
- int userId = ActivityManager.getCurrentUser();
- resetUserSwitcherEnabled();
-
- mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, true, userId);
- assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
-
- mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, false, userId);
- assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
- }
-
- @Test
- public void assertIsUserSwitcherEnabledOnDemoMode() throws Exception {
- int userId = ActivityManager.getCurrentUser();
- resetUserSwitcherEnabled();
-
- setDeviceDemoMode(true);
- assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
-
- setDeviceDemoMode(false);
- assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
- }
-
- private void resetUserSwitcherEnabled() throws Exception {
- int userId = ActivityManager.getCurrentUser();
- setUserSwitch(true);
- setShowMultiuserUI(true);
- setDeviceDemoMode(false);
- setMaxSupportedUsers(8);
- mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, false, userId);
- }
-
- private void setUserSwitch(boolean enabled) {
- android.provider.Settings.Global.putInt(mContext.getContentResolver(),
- android.provider.Settings.Global.USER_SWITCHER_ENABLED, enabled ? 1 : 0);
- }
-
- private void setDeviceDemoMode(boolean enabled) {
- android.provider.Settings.Global.putInt(mContext.getContentResolver(),
- android.provider.Settings.Global.DEVICE_DEMO_MODE, enabled ? 1 : 0);
- }
-
-
private static String runShellCommand(String cmd) throws Exception {
return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
.executeShellCommand(cmd);
}
-
- private static String setSystemProperty(String name, String value) throws Exception {
- final String oldValue = runShellCommand("getprop " + name);
- assertThat(runShellCommand("setprop " + name + " " + value))
- .isEqualTo("");
- return oldValue;
- }
-
- private static void setMaxSupportedUsers(int max) throws Exception {
- setSystemProperty("fw.max_users", String.valueOf(max));
- }
-
- public static void setShowMultiuserUI(boolean show) throws Exception {
- setSystemProperty("fw.show_multiuserui", String.valueOf(show));
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 7d0f361..dd681aa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -265,6 +265,52 @@
assertWithMessage("Communal profile not visible").that(umCommunal.isUserVisible()).isTrue();
}
+ @Test
+ public void testPrivateProfile() throws Exception {
+ UserHandle mainUser = mUserManager.getMainUser();
+ assumeTrue("Main user is null", mainUser != null);
+ // Get the default properties for private profile user type.
+ final UserTypeDetails userTypeDetails =
+ UserTypeFactory.getUserTypes().get(UserManager.USER_TYPE_PROFILE_PRIVATE);
+ assertWithMessage("No %s type on device", UserManager.USER_TYPE_PROFILE_PRIVATE)
+ .that(userTypeDetails).isNotNull();
+ final UserProperties typeProps = userTypeDetails.getDefaultUserPropertiesReference();
+
+ // Test that only one private profile can be created
+ final int mainUserId = mainUser.getIdentifier();
+ UserInfo userInfo = createProfileForUser("Private profile1",
+ UserManager.USER_TYPE_PROFILE_PRIVATE,
+ mainUserId);
+ assertThat(userInfo).isNotNull();
+ UserInfo userInfo2 = createProfileForUser("Private profile2",
+ UserManager.USER_TYPE_PROFILE_PRIVATE,
+ mainUserId);
+ assertThat(userInfo2).isNull();
+
+ // Check that the new private profile has the expected properties (relative to the defaults)
+ // provided that the test caller has the necessary permissions.
+ UserProperties privateProfileUserProperties =
+ mUserManager.getUserProperties(UserHandle.of(userInfo.id));
+ assertThat(typeProps.getShowInLauncher())
+ .isEqualTo(privateProfileUserProperties.getShowInLauncher());
+ assertThrows(SecurityException.class, privateProfileUserProperties::getStartWithParent);
+ assertThrows(SecurityException.class,
+ privateProfileUserProperties::getCrossProfileIntentFilterAccessControl);
+ assertThat(typeProps.isMediaSharedWithParent())
+ .isEqualTo(privateProfileUserProperties.isMediaSharedWithParent());
+ assertThat(typeProps.isCredentialShareableWithParent())
+ .isEqualTo(privateProfileUserProperties.isCredentialShareableWithParent());
+ assertThrows(SecurityException.class, privateProfileUserProperties::getDeleteAppWithParent);
+
+ // Verify private profile parent
+ assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
+ UserInfo parentProfileInfo = mUserManager.getProfileParent(userInfo.id);
+ assertThat(parentProfileInfo).isNotNull();
+ assertThat(mainUserId).isEqualTo(parentProfileInfo.id);
+ removeUser(userInfo.id);
+ assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
+ }
+
@MediumTest
@Test
public void testAdd2Users() throws Exception {
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLoggingLatencyTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLoggingLatencyTest.java
index fc4e6e0..efe1af3 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLoggingLatencyTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLoggingLatencyTest.java
@@ -38,6 +38,7 @@
import android.os.Process;
import android.os.RemoteException;
+import androidx.test.filters.FlakyTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.util.FakeLatencyTracker;
@@ -47,6 +48,7 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.Timeout;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
@@ -54,10 +56,13 @@
import org.mockito.MockitoAnnotations;
@RunWith(JUnit4.class)
+@FlakyTest(bugId = 275746222)
public class SoundTriggerMiddlewareLoggingLatencyTest {
@Rule
public TestableDeviceConfigRule mDeviceConfigRule = new TestableDeviceConfigRule();
+ @Rule
+ public Timeout mGlobalTimeout = Timeout.seconds(30);
private FakeLatencyTracker mLatencyTracker;
@Mock
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index 280afba..3bb86a7 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -316,4 +316,16 @@
pressKey(KEYCODE_POWER, 0 /* pressTime */);
assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
}
+
+ // Verify short press should not be triggered if no very long press behavior defined but the
+ // press time exceeded the very long press timeout.
+ @Test
+ public void testTimeoutExceedVeryLongPress() throws InterruptedException {
+ mVeryLongPressOnPowerBehavior = false;
+
+ pressKey(KEYCODE_POWER, mVeryLongPressTime + 50);
+ assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+ assertEquals(mVeryLongPressed.getCount(), 1);
+ assertEquals(mShortPressed.getCount(), 1);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 204cbf7..994dcf1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -31,17 +31,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import android.app.StatusBarManager;
@@ -268,8 +263,6 @@
navBar.setHasSurface(true);
navBarProvider.setServerVisible(true);
final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
- spyOn(policy);
- doNothing().when(policy).startAnimation(anyBoolean(), any());
// Make both system bars invisible.
mAppWindow.setRequestedVisibleTypes(
@@ -305,8 +298,6 @@
addNavigationBar().getControllableInsetProvider().setServerVisible(true);
final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
- spyOn(policy);
- doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(mAppWindow);
policy.showTransient(navigationBars() | statusBars(),
true /* isGestureOnSystemBar */);
@@ -341,8 +332,6 @@
mAppWindow.mAboveInsetsState.addSource(navBarSource);
mAppWindow.mAboveInsetsState.addSource(statusBarSource);
final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
- spyOn(policy);
- doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(mAppWindow);
policy.showTransient(navigationBars() | statusBars(),
true /* isGestureOnSystemBar */);
@@ -390,8 +379,6 @@
final WindowState app2 = addWindow(TYPE_APPLICATION, "app");
final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
- spyOn(policy);
- doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(app);
policy.showTransient(navigationBars() | statusBars(),
true /* isGestureOnSystemBar */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
index 41bfc80..1a4b94b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
@@ -98,7 +98,7 @@
mView1 = new Button(mActivity);
mView2 = new Button(mActivity);
- mActivity.runOnUiThread(() -> {
+ mInstrumentation.runOnMainSync(() -> {
TestWindowlessWindowManager wwm = new TestWindowlessWindowManager(
mActivity.getResources().getConfiguration(), sc, null);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 7edfd9a..e252b9e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -208,7 +208,11 @@
spyOn(mContext);
doReturn(null).when(mContext)
- .registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class));
+ .registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class),
+ nullable(String.class), nullable(Handler.class));
+ doReturn(null).when(mContext)
+ .registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class),
+ nullable(String.class), nullable(Handler.class), anyInt());
doReturn(null).when(mContext)
.registerReceiverAsUser(any(BroadcastReceiver.class), any(UserHandle.class),
any(IntentFilter.class), nullable(String.class), nullable(Handler.class));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index 4639ee0..45ecc3f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -77,6 +77,10 @@
spyOn(inputMonitor);
doNothing().when(inputMonitor).resumeDispatchingLw(any());
+ final InsetsPolicy insetsPolicy = getInsetsPolicy();
+ WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget());
+ WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getTransientControlTarget());
+
// For devices that set the sysprop ro.bootanim.set_orientation_<display_id>
// See DisplayRotation#readDefaultDisplayRotation for context.
// Without that, meaning of height and width in context of the tests can be swapped if
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index ed0c8ef..7351e81 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -114,7 +114,11 @@
private BLASTSyncEngine mSyncEngine;
private Transition createTestTransition(int transitType, TransitionController controller) {
- return new Transition(transitType, 0 /* flags */, controller, controller.mSyncEngine);
+ final Transition transition = new Transition(transitType, 0 /* flags */, controller,
+ controller.mSyncEngine);
+ spyOn(transition.mLogger);
+ doNothing().when(transition.mLogger).logOnSendAsync(any());
+ return transition;
}
private Transition createTestTransition(int transitType) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index be8ee78..d5547ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -222,6 +222,10 @@
displayPolicy.finishWindowsDrawn();
displayPolicy.finishScreenTurningOn();
+ final InsetsPolicy insetsPolicy = mDefaultDisplay.getInsetsPolicy();
+ suppressInsetsAnimation(insetsPolicy.getTransientControlTarget());
+ suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget());
+
mTransaction = mSystemServicesTestRule.mTransaction;
mMockSession = mock(Session.class);
@@ -278,6 +282,15 @@
checkDeviceSpecificOverridesNotApplied();
}
+ /**
+ * The test doesn't create real SurfaceControls, but mocked ones. This prevents the target from
+ * controlling them, or it will cause {@link NullPointerException}.
+ */
+ static void suppressInsetsAnimation(InsetsControlTarget target) {
+ spyOn(target);
+ Mockito.doNothing().when(target).notifyInsetsControlChanged();
+ }
+
@After
public void tearDown() throws Exception {
if (mUseFakeSettingsProvider) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d421df8..f86d2b5 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -50,7 +50,6 @@
import android.telephony.ims.RcsUceAdapter;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
import com.android.internal.telephony.ICarrierConfigLoader;
import com.android.telephony.Rlog;
@@ -5820,6 +5819,21 @@
*/
public static final int NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED = 3;
+ /**
+ * This specifies whether the carrier support the global number format or not.
+ * {@link SubscriptionManager#getPhoneNumber(int)},
+ * {@link SubscriptionManager#getPhoneNumber(int, int)} with
+ * {@link SubscriptionManager#PHONE_NUMBER_SOURCE_IMS}
+ * In order to provide the phone number to the APIs, the framework extracts the phone
+ * number from the message received from the carrier server. If the carrier does not use
+ * global number format, the framework could not provide phone number.
+ * <p>
+ * If not set or set to false value, the framework handle only global number format URI.
+ * @hide
+ */
+ public static final String KEY_ALLOW_NON_GLOBAL_PHONE_NUMBER_FORMAT_BOOL =
+ KEY_PREFIX + "allow_non_global_phone_number_format_bool";
+
private Ims() {}
private static PersistableBundle getDefaults() {
@@ -5932,6 +5946,8 @@
defaults.putString(KEY_IMS_USER_AGENT_STRING,
"#MANUFACTURER#_#MODEL#_Android#AV#_#BUILD#");
+ defaults.putBoolean(KEY_ALLOW_NON_GLOBAL_PHONE_NUMBER_FORMAT_BOOL, false);
+
return defaults;
}
}
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index b5c1d7d..c7f0c5f 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -36,8 +36,10 @@
import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Description of the response of a setup data call connection request.
@@ -172,63 +174,57 @@
@Nullable List<InetAddress> dnsAddresses,
@Nullable List<InetAddress> gatewayAddresses,
@Nullable List<InetAddress> pcscfAddresses, int mtu) {
- mCause = cause;
- mSuggestedRetryTime = suggestedRetryTime;
- mId = id;
- mLinkStatus = linkStatus;
- mProtocolType = protocolType;
- mInterfaceName = (interfaceName == null) ? "" : interfaceName;
- mAddresses = (addresses == null)
- ? new ArrayList<>() : new ArrayList<>(addresses);
- mDnsAddresses = (dnsAddresses == null)
- ? new ArrayList<>() : new ArrayList<>(dnsAddresses);
- mGatewayAddresses = (gatewayAddresses == null)
- ? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
- mPcscfAddresses = (pcscfAddresses == null)
- ? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
- mMtu = mMtuV4 = mMtuV6 = mtu;
- mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY;
- mPduSessionId = PDU_SESSION_ID_NOT_SET;
- mDefaultQos = null;
- mQosBearerSessions = new ArrayList<>();
- mSliceInfo = null;
- mTrafficDescriptors = new ArrayList<>();
+ this(cause, suggestedRetryTime, id,
+ linkStatus, protocolType,
+ interfaceName == null ? "" : interfaceName,
+ addresses == null ? Collections.emptyList() : addresses,
+ dnsAddresses == null ? Collections.emptyList() : dnsAddresses,
+ gatewayAddresses == null ? Collections.emptyList() : gatewayAddresses,
+ pcscfAddresses == null ? Collections.emptyList() : pcscfAddresses,
+ mtu, mtu /* mtuV4 */, mtu /* mtuV6 */,
+ HANDOVER_FAILURE_MODE_LEGACY, PDU_SESSION_ID_NOT_SET,
+ null /* defaultQos */, Collections.emptyList() /* qosBearerSessions */,
+ null /* sliceInfo */,
+ Collections.emptyList() /* trafficDescriptors */);
}
private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
@LinkStatus int linkStatus, @ProtocolType int protocolType,
- @Nullable String interfaceName, @Nullable List<LinkAddress> addresses,
- @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
- @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
+ @NonNull String interfaceName, @NonNull List<LinkAddress> addresses,
+ @NonNull List<InetAddress> dnsAddresses, @NonNull List<InetAddress> gatewayAddresses,
+ @NonNull List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
@HandoverFailureMode int handoverFailureMode, int pduSessionId,
- @Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions,
+ @Nullable Qos defaultQos, @NonNull List<QosBearerSession> qosBearerSessions,
@Nullable NetworkSliceInfo sliceInfo,
- @Nullable List<TrafficDescriptor> trafficDescriptors) {
+ @NonNull List<TrafficDescriptor> trafficDescriptors) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
mId = id;
mLinkStatus = linkStatus;
mProtocolType = protocolType;
- mInterfaceName = (interfaceName == null) ? "" : interfaceName;
- mAddresses = (addresses == null)
- ? new ArrayList<>() : new ArrayList<>(addresses);
- mDnsAddresses = (dnsAddresses == null)
- ? new ArrayList<>() : new ArrayList<>(dnsAddresses);
- mGatewayAddresses = (gatewayAddresses == null)
- ? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
- mPcscfAddresses = (pcscfAddresses == null)
- ? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
+ mInterfaceName = interfaceName;
+ mAddresses = new ArrayList<>(addresses);
+ mDnsAddresses = new ArrayList<>(dnsAddresses);
+ mGatewayAddresses = new ArrayList<>(gatewayAddresses);
+ mPcscfAddresses = new ArrayList<>(pcscfAddresses);
mMtu = mtu;
mMtuV4 = mtuV4;
mMtuV6 = mtuV6;
mHandoverFailureMode = handoverFailureMode;
mPduSessionId = pduSessionId;
mDefaultQos = defaultQos;
- mQosBearerSessions = (qosBearerSessions == null)
- ? new ArrayList<>() : new ArrayList<>(qosBearerSessions);
+ mQosBearerSessions = new ArrayList<>(qosBearerSessions);
mSliceInfo = sliceInfo;
- mTrafficDescriptors = (trafficDescriptors == null)
- ? new ArrayList<>() : new ArrayList<>(trafficDescriptors);
+ mTrafficDescriptors = new ArrayList<>(trafficDescriptors);
+
+ if (mLinkStatus == LINK_STATUS_ACTIVE
+ || mLinkStatus == LINK_STATUS_DORMANT) {
+ Objects.requireNonNull(
+ mInterfaceName, "Active data calls must be on a valid interface!");
+ if (mCause != DataFailCause.NONE) {
+ throw new IllegalStateException("Active data call must not have a failure!");
+ }
+ }
}
/** @hide */
@@ -241,24 +237,39 @@
mProtocolType = source.readInt();
mInterfaceName = source.readString();
mAddresses = new ArrayList<>();
- source.readList(mAddresses, LinkAddress.class.getClassLoader(), android.net.LinkAddress.class);
+ source.readList(mAddresses,
+ LinkAddress.class.getClassLoader(),
+ android.net.LinkAddress.class);
mDnsAddresses = new ArrayList<>();
- source.readList(mDnsAddresses, InetAddress.class.getClassLoader(), java.net.InetAddress.class);
+ source.readList(mDnsAddresses,
+ InetAddress.class.getClassLoader(),
+ java.net.InetAddress.class);
mGatewayAddresses = new ArrayList<>();
- source.readList(mGatewayAddresses, InetAddress.class.getClassLoader(), java.net.InetAddress.class);
+ source.readList(mGatewayAddresses,
+ InetAddress.class.getClassLoader(),
+ java.net.InetAddress.class);
mPcscfAddresses = new ArrayList<>();
- source.readList(mPcscfAddresses, InetAddress.class.getClassLoader(), java.net.InetAddress.class);
+ source.readList(mPcscfAddresses,
+ InetAddress.class.getClassLoader(),
+ java.net.InetAddress.class);
mMtu = source.readInt();
mMtuV4 = source.readInt();
mMtuV6 = source.readInt();
mHandoverFailureMode = source.readInt();
mPduSessionId = source.readInt();
- mDefaultQos = source.readParcelable(Qos.class.getClassLoader(), android.telephony.data.Qos.class);
+ mDefaultQos = source.readParcelable(Qos.class.getClassLoader(),
+ android.telephony.data.Qos.class);
mQosBearerSessions = new ArrayList<>();
- source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader(), android.telephony.data.QosBearerSession.class);
- mSliceInfo = source.readParcelable(NetworkSliceInfo.class.getClassLoader(), android.telephony.data.NetworkSliceInfo.class);
+ source.readList(mQosBearerSessions,
+ QosBearerSession.class.getClassLoader(),
+ android.telephony.data.QosBearerSession.class);
+ mSliceInfo = source.readParcelable(
+ NetworkSliceInfo.class.getClassLoader(),
+ android.telephony.data.NetworkSliceInfo.class);
mTrafficDescriptors = new ArrayList<>();
- source.readList(mTrafficDescriptors, TrafficDescriptor.class.getClassLoader(), android.telephony.data.TrafficDescriptor.class);
+ source.readList(mTrafficDescriptors,
+ TrafficDescriptor.class.getClassLoader(),
+ android.telephony.data.TrafficDescriptor.class);
}
/**
@@ -322,28 +333,36 @@
* @return A list of addresses of this data connection.
*/
@NonNull
- public List<LinkAddress> getAddresses() { return mAddresses; }
+ public List<LinkAddress> getAddresses() {
+ return Collections.unmodifiableList(mAddresses);
+ }
/**
* @return A list of DNS server addresses, e.g., "192.0.1.3" or
* "192.0.1.11 2001:db8::1". Empty list if no dns server addresses returned.
*/
@NonNull
- public List<InetAddress> getDnsAddresses() { return mDnsAddresses; }
+ public List<InetAddress> getDnsAddresses() {
+ return Collections.unmodifiableList(mDnsAddresses);
+ }
/**
* @return A list of default gateway addresses, e.g., "192.0.1.3" or
* "192.0.1.11 2001:db8::1". Empty list if the addresses represent point to point connections.
*/
@NonNull
- public List<InetAddress> getGatewayAddresses() { return mGatewayAddresses; }
+ public List<InetAddress> getGatewayAddresses() {
+ return Collections.unmodifiableList(mGatewayAddresses);
+ }
/**
* @return A list of Proxy Call State Control Function address via PCO (Protocol Configuration
* Option) for IMS client.
*/
@NonNull
- public List<InetAddress> getPcscfAddresses() { return mPcscfAddresses; }
+ public List<InetAddress> getPcscfAddresses() {
+ return Collections.unmodifiableList(mPcscfAddresses);
+ }
/**
* @return MTU (maximum transmission unit) in bytes received from network. Zero or negative
@@ -404,7 +423,7 @@
*/
@NonNull
public List<QosBearerSession> getQosBearerSessions() {
- return mQosBearerSessions;
+ return Collections.unmodifiableList(mQosBearerSessions);
}
/**
@@ -420,7 +439,7 @@
*/
@NonNull
public List<TrafficDescriptor> getTrafficDescriptors() {
- return mTrafficDescriptors;
+ return Collections.unmodifiableList(mTrafficDescriptors);
}
@NonNull
@@ -461,18 +480,6 @@
DataCallResponse other = (DataCallResponse) o;
- final boolean isQosBearerSessionsSame =
- (mQosBearerSessions == null || other.mQosBearerSessions == null)
- ? mQosBearerSessions == other.mQosBearerSessions
- : (mQosBearerSessions.size() == other.mQosBearerSessions.size()
- && mQosBearerSessions.containsAll(other.mQosBearerSessions));
-
- final boolean isTrafficDescriptorsSame =
- (mTrafficDescriptors == null || other.mTrafficDescriptors == null)
- ? mTrafficDescriptors == other.mTrafficDescriptors
- : (mTrafficDescriptors.size() == other.mTrafficDescriptors.size()
- && mTrafficDescriptors.containsAll(other.mTrafficDescriptors));
-
return mCause == other.mCause
&& mSuggestedRetryTime == other.mSuggestedRetryTime
&& mId == other.mId
@@ -493,42 +500,20 @@
&& mHandoverFailureMode == other.mHandoverFailureMode
&& mPduSessionId == other.mPduSessionId
&& Objects.equals(mDefaultQos, other.mDefaultQos)
- && isQosBearerSessionsSame
+ && mQosBearerSessions.size() == other.mQosBearerSessions.size() // non-null
+ && mQosBearerSessions.containsAll(other.mQosBearerSessions) // non-null
&& Objects.equals(mSliceInfo, other.mSliceInfo)
- && isTrafficDescriptorsSame;
+ && mTrafficDescriptors.size() == other.mTrafficDescriptors.size() // non-null
+ && mTrafficDescriptors.containsAll(other.mTrafficDescriptors); // non-null
}
@Override
public int hashCode() {
- // Generate order-independent hashes for lists
- int addressesHash = mAddresses.stream()
- .map(LinkAddress::hashCode)
- .mapToInt(Integer::intValue)
- .sum();
- int dnsAddressesHash = mDnsAddresses.stream()
- .map(InetAddress::hashCode)
- .mapToInt(Integer::intValue)
- .sum();
- int gatewayAddressesHash = mGatewayAddresses.stream()
- .map(InetAddress::hashCode)
- .mapToInt(Integer::intValue)
- .sum();
- int pcscfAddressesHash = mPcscfAddresses.stream()
- .map(InetAddress::hashCode)
- .mapToInt(Integer::intValue)
- .sum();
- int qosBearerSessionsHash = mQosBearerSessions.stream()
- .map(QosBearerSession::hashCode)
- .mapToInt(Integer::intValue)
- .sum();
- int trafficDescriptorsHash = mTrafficDescriptors.stream()
- .map(TrafficDescriptor::hashCode)
- .mapToInt(Integer::intValue)
- .sum();
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
- mInterfaceName, addressesHash, dnsAddressesHash, gatewayAddressesHash,
- pcscfAddressesHash, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
- mDefaultQos, qosBearerSessionsHash, mSliceInfo, trafficDescriptorsHash);
+ mInterfaceName, Set.copyOf(mAddresses), Set.copyOf(mDnsAddresses),
+ Set.copyOf(mGatewayAddresses), Set.copyOf(mPcscfAddresses), mMtu, mMtuV4, mMtuV6,
+ mHandoverFailureMode, mPduSessionId, mDefaultQos, Set.copyOf(mQosBearerSessions),
+ mSliceInfo, Set.copyOf(mTrafficDescriptors));
}
@Override
@@ -616,15 +601,15 @@
private @ProtocolType int mProtocolType;
- private String mInterfaceName;
+ private String mInterfaceName = "";
- private List<LinkAddress> mAddresses;
+ private List<LinkAddress> mAddresses = Collections.emptyList();
- private List<InetAddress> mDnsAddresses;
+ private List<InetAddress> mDnsAddresses = Collections.emptyList();
- private List<InetAddress> mGatewayAddresses;
+ private List<InetAddress> mGatewayAddresses = Collections.emptyList();
- private List<InetAddress> mPcscfAddresses;
+ private List<InetAddress> mPcscfAddresses = Collections.emptyList();
private int mMtu;
@@ -636,11 +621,11 @@
private int mPduSessionId = PDU_SESSION_ID_NOT_SET;
- private Qos mDefaultQos;
+ private @Nullable Qos mDefaultQos;
private List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
- private NetworkSliceInfo mSliceInfo;
+ private @Nullable NetworkSliceInfo mSliceInfo;
private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
@@ -653,7 +638,9 @@
/**
* Set data call fail cause.
*
- * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
+ * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error, which
+ * is the only valid value for data calls that are {@link LINK_STATUS_ACTIVE} or
+ * {@link LINK_STATUS_DORMANT}.
* @return The same instance of the builder.
*/
public @NonNull Builder setCause(@DataFailureCause int cause) {
@@ -722,10 +709,13 @@
/**
* Set the network interface name.
*
- * @param interfaceName The network interface name (e.g. "rmnet_data1").
+ * @param interfaceName The network interface name (e.g. "rmnet_data1"). This value may not
+ * be null for valid data calls (those that are {@link LINK_STATUS_ACTIVE} or
+ * {@link LINK_STATUS_DORMANT}).
* @return The same instance of the builder.
*/
- public @NonNull Builder setInterfaceName(@NonNull String interfaceName) {
+ public @NonNull Builder setInterfaceName(@Nullable String interfaceName) {
+ if (interfaceName == null) interfaceName = "";
mInterfaceName = interfaceName;
return this;
}
@@ -737,6 +727,7 @@
* @return The same instance of the builder.
*/
public @NonNull Builder setAddresses(@NonNull List<LinkAddress> addresses) {
+ Objects.requireNonNull(addresses);
mAddresses = addresses;
return this;
}
@@ -748,6 +739,7 @@
* @return The same instance of the builder.
*/
public @NonNull Builder setDnsAddresses(@NonNull List<InetAddress> dnsAddresses) {
+ Objects.requireNonNull(dnsAddresses);
mDnsAddresses = dnsAddresses;
return this;
}
@@ -759,6 +751,7 @@
* @return The same instance of the builder.
*/
public @NonNull Builder setGatewayAddresses(@NonNull List<InetAddress> gatewayAddresses) {
+ Objects.requireNonNull(gatewayAddresses);
mGatewayAddresses = gatewayAddresses;
return this;
}
@@ -771,6 +764,7 @@
* @return The same instance of the builder.
*/
public @NonNull Builder setPcscfAddresses(@NonNull List<InetAddress> pcscfAddresses) {
+ Objects.requireNonNull(pcscfAddresses);
mPcscfAddresses = pcscfAddresses;
return this;
}
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 8faedeb..feae3b7 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -108,6 +108,7 @@
":FlickerTestsAppLaunch-src",
":FlickerTestsQuickswitch-src",
":FlickerTestsRotation-src",
+ ":FlickerTestsNotification-src",
],
}
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/AndroidTestTemplate.xml
index 1ede943..ed63ec0 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AndroidTestTemplate.xml
@@ -85,6 +85,8 @@
value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/>
<option name="directory-keys"
value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker.notification/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 865d5b4..dbbc771 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.close
import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
index c108633..566f393 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.close
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index ea9710c6..ed930fc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.close
import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
index d65555a..49ed183 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.close
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
index 2563bfb..4fd4a61 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
index 33302fa..e39a578 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
index 45fb453..6d0b6f4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
index 12ee7d0..d2c3807 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
index 1371fd7..3e0958a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.launch
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 46eb257..2a2a3db 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -21,7 +21,7 @@
import android.tools.common.NavBar
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 1497e50..eec6bfd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
index 9b6c136..ab6a1ea 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.launch
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
index 4a1bd7e..1bdb6e71 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
@@ -18,6 +18,8 @@
import android.os.SystemClock
import android.platform.test.annotations.Postsubmit
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.CameraAppHelper
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -137,8 +139,14 @@
@Postsubmit
@Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS +
+ listOf(CAMERA_BACKGROUND)
+ )
+ }
+ }
@Postsubmit
@Test
@@ -161,5 +169,12 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+
+ private val CAMERA_BACKGROUND =
+ ComponentNameMatcher(
+ "Background for SurfaceView" +
+ "[com.google.android.GoogleCamera/" +
+ "com.google.android.apps.camera.legacy.app.activity.main.CameraActivity]"
+ )
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 39c8ca0..4164c0d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -121,7 +121,7 @@
@FlakyTest(bugId = 265007895)
@Test
fun transitionHasColorBackground() {
- val backgroundColorLayer = ComponentNameMatcher("", "Animation Background")
+ val backgroundColorLayer = ComponentNameMatcher("", "animation-background")
val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
flicker.assertLayers {
this.invoke("LAUNCH_NEW_TASK_ACTIVITY coversExactly displayBounds") {
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
index f4a04a1..b6b9924 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
@@ -97,6 +97,8 @@
assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
// Do not run on TV. Direct Reply isn't supported on TV.
assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY));
+ // Do not run on Wear. Direct Reply isn't supported on Wear.
+ assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_WATCH));
}
@After