Merge "Temporary fix DialogLaunchAnimatorTest#testShowDialogFromView" into sc-v2-dev
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
index 1c40a06..42fb24f 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
@@ -217,7 +217,7 @@
}
@Override
- public void onTaskAppeared(RemoteAnimationTarget app) throws RemoteException {
+ public void onTasksAppeared(RemoteAnimationTarget[] app) throws RemoteException {
/* no-op */
}
};
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 40880c1..e3a6dd0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -113,7 +113,7 @@
method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
method public static void resumeAppSwitches() throws android.os.RemoteException;
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setStopBackgroundUsersOnSwitch(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setStopUserOnSwitch(int);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean stopUser(int, boolean);
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.DUMP) public void waitForBroadcastIdle();
@@ -128,9 +128,9 @@
field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0
field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4
field public static final int PROCESS_STATE_TOP = 2; // 0x2
- field public static final int STOP_BG_USERS_ON_SWITCH_DEFAULT = -1; // 0xffffffff
- field public static final int STOP_BG_USERS_ON_SWITCH_FALSE = 0; // 0x0
- field public static final int STOP_BG_USERS_ON_SWITCH_TRUE = 1; // 0x1
+ field public static final int STOP_USER_ON_SWITCH_DEFAULT = -1; // 0xffffffff
+ field public static final int STOP_USER_ON_SWITCH_FALSE = 0; // 0x0
+ field public static final int STOP_USER_ON_SWITCH_TRUE = 1; // 0x1
}
public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable {
@@ -785,6 +785,7 @@
field public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 1.7777778f;
field public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // 0xabf91bdL
field public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 1.5f;
+ field public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // 0xc2368d6L
field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 50a562b..f0deeca 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4081,45 +4081,46 @@
* @hide
*/
@TestApi
- public static final int STOP_BG_USERS_ON_SWITCH_DEFAULT = -1;
+ public static final int STOP_USER_ON_SWITCH_DEFAULT = -1;
/**
- * Overrides value defined by the platform and stop background users on switch.
+ * Overrides value defined by the platform and stop user on switch.
*
* @hide
*/
@TestApi
- public static final int STOP_BG_USERS_ON_SWITCH_TRUE = 1;
+ public static final int STOP_USER_ON_SWITCH_TRUE = 1;
/**
- * Overrides value defined by the platform and don't stop background users on switch.
+ * Overrides value defined by the platform and don't stop user on switch.
*
* @hide
*/
@TestApi
- public static final int STOP_BG_USERS_ON_SWITCH_FALSE = 0;
+ public static final int STOP_USER_ON_SWITCH_FALSE = 0;
/** @hide */
- @IntDef(prefix = { "STOP_BG_USERS_ON_SWITCH_" }, value = {
- STOP_BG_USERS_ON_SWITCH_DEFAULT,
- STOP_BG_USERS_ON_SWITCH_TRUE,
- STOP_BG_USERS_ON_SWITCH_FALSE
+ @IntDef(prefix = { "STOP_USER_ON_SWITCH_" }, value = {
+ STOP_USER_ON_SWITCH_DEFAULT,
+ STOP_USER_ON_SWITCH_TRUE,
+ STOP_USER_ON_SWITCH_FALSE
})
- public @interface StopBgUsersOnSwitch {}
+ public @interface StopUserOnSwitch {}
/**
- * Sets whether background users should be stopped when the current user is switched.
+ * Sets whether the current foreground user (and its profiles) should be stopped after switched
+ * out.
*
- * <p>Should only be used on tests.
+ * <p>Should only be used on tests. Doesn't apply to {@link UserHandle#SYSTEM system user}.
*
* @hide
*/
@TestApi
@RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
- public void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+ public void setStopUserOnSwitch(@StopUserOnSwitch int value) {
try {
- getService().setStopBackgroundUsersOnSwitch(value);
+ getService().setStopUserOnSwitch(value);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index b416c95..7be4c3e 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -16,7 +16,7 @@
package android.app;
-import static android.app.ActivityManager.StopBgUsersOnSwitch;
+import static android.app.ActivityManager.StopUserOnSwitch;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -663,9 +663,10 @@
@Nullable VoiceInteractionManagerProvider provider);
/**
- * Sets whether background users should be stopped when the current user is switched.
+ * Sets whether the current foreground user (and its profiles) should be stopped after switched
+ * out.
*/
- public abstract void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value);
+ public abstract void setStopUserOnSwitch(@StopUserOnSwitch int value);
/**
* Provides the interface to communicate between voice interaction manager service and
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 81f0b44..431755e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -29,7 +29,6 @@
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
@@ -315,7 +314,7 @@
@UnsupportedAppUsage
private ContextImpl mSystemContext;
- private final SparseArray<ContextImpl> mDisplaySystemUiContexts = new SparseArray<>();
+ private ContextImpl mSystemUiContext;
@UnsupportedAppUsage
static volatile IPackageManager sPackageManager;
@@ -2612,26 +2611,22 @@
}
@Override
- @NonNull
public ContextImpl getSystemUiContext() {
- return getSystemUiContext(DEFAULT_DISPLAY);
+ synchronized (this) {
+ if (mSystemUiContext == null) {
+ mSystemUiContext = ContextImpl.createSystemUiContext(getSystemContext());
+ }
+ return mSystemUiContext;
+ }
}
/**
- * Gets the context instance base on system resources & display information which used for UI.
+ * Create the context instance base on system resources & display information which used for UI.
* @param displayId The ID of the display where the UI is shown.
* @see ContextImpl#createSystemUiContext(ContextImpl, int)
*/
- @NonNull
- public ContextImpl getSystemUiContext(int displayId) {
- synchronized (this) {
- ContextImpl systemUiContext = mDisplaySystemUiContexts.get(displayId);
- if (systemUiContext == null) {
- systemUiContext = ContextImpl.createSystemUiContext(getSystemContext(), displayId);
- mDisplaySystemUiContexts.put(displayId, systemUiContext);
- }
- return systemUiContext;
- }
+ public ContextImpl createSystemUiContext(int displayId) {
+ return ContextImpl.createSystemUiContext(getSystemUiContext(), displayId);
}
public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
@@ -3750,7 +3745,7 @@
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
for (int id : dm.getDisplayIds()) {
- if (id != DEFAULT_DISPLAY) {
+ if (id != Display.DEFAULT_DISPLAY) {
Display display =
dm.getCompatibleDisplay(id, appContext.getResources());
appContext = (ContextImpl) appContext.createDisplayContext(display);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ed496c6..b5ed171 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2618,10 +2618,7 @@
overrideConfig, display.getDisplayAdjustments().getCompatibilityInfo(),
mResources.getLoaders()));
context.mDisplay = display;
- // Inherit context type if the container is from System or System UI context to bypass
- // UI context check.
- context.mContextType = mContextType == CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
- ? CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI : CONTEXT_TYPE_DISPLAY_CONTEXT;
+ context.mContextType = CONTEXT_TYPE_DISPLAY_CONTEXT;
// Display contexts and any context derived from a display context should always override
// the display that would otherwise be inherited from mToken (or the global configuration if
// mToken is null).
@@ -2674,8 +2671,7 @@
// Step 2. Create the base context of the window context, it will also create a Resources
// associated with the WindowTokenClient and set the token to the base context.
- final ContextImpl windowContextBase = createWindowContextBase(windowTokenClient,
- display.getDisplayId());
+ final ContextImpl windowContextBase = createWindowContextBase(windowTokenClient, display);
// Step 3. Create a WindowContext instance and set it as the outer context of the base
// context to make the service obtained by #getSystemService(String) able to query
@@ -2700,7 +2696,9 @@
if (display == null) {
throw new IllegalArgumentException("Display must not be null");
}
- return createWindowContextBase(token, display.getDisplayId());
+ final ContextImpl tokenContext = createWindowContextBase(token, display);
+ tokenContext.setResources(createWindowContextResources(tokenContext));
+ return tokenContext;
}
/**
@@ -2708,13 +2706,13 @@
* window.
*
* @param token The token to associate with {@link Resources}
- * @param displayId The ID of {@link Display} to associate with.
+ * @param display The {@link Display} to associate with.
*
* @see #createWindowContext(Display, int, Bundle)
* @see #createTokenContext(IBinder, Display)
*/
@UiContext
- ContextImpl createWindowContextBase(@NonNull IBinder token, int displayId) {
+ ContextImpl createWindowContextBase(@NonNull IBinder token, @NonNull Display display) {
ContextImpl baseContext = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
mAttributionSource.getAttributionTag(),
mAttributionSource.getNext(),
@@ -2728,8 +2726,8 @@
baseContext.setResources(windowContextResources);
// Associate the display with window context resources so that configuration update from
// the server side will also apply to the display's metrics.
- baseContext.mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(displayId,
- windowContextResources);
+ baseContext.mDisplay = ResourcesManager.getInstance()
+ .getAdjustedDisplay(display.getDisplayId(), windowContextResources);
return baseContext;
}
@@ -2965,16 +2963,6 @@
mContentCaptureOptions = options;
}
- @Override
- protected void finalize() throws Throwable {
- // If token is a WindowTokenClient, the Context is usually associated with a
- // WindowContainer. We should detach from WindowContainer when the Context is finalized.
- if (mToken instanceof WindowTokenClient) {
- ((WindowTokenClient) mToken).detachFromWindowContainerIfNeeded();
- }
- super.finalize();
- }
-
@UnsupportedAppUsage
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
@@ -2995,15 +2983,24 @@
* @param displayId The ID of the display where the UI is shown.
*/
static ContextImpl createSystemUiContext(ContextImpl systemContext, int displayId) {
- final WindowTokenClient token = new WindowTokenClient();
- ContextImpl context = systemContext.createWindowContextBase(token, displayId);
- token.attachContext(context);
- token.attachToDisplayContent(displayId);
+ final LoadedApk packageInfo = systemContext.mPackageInfo;
+ ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo,
+ ContextParams.EMPTY, null, null, null, null, null, 0, null, null);
+ context.setResources(createResources(null, packageInfo, null, displayId, null,
+ packageInfo.getCompatibilityInfo(), null));
+ context.updateDisplay(displayId);
context.mContextType = CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI;
-
return context;
}
+ /**
+ * The overloaded method of {@link #createSystemUiContext(ContextImpl, int)}.
+ * Uses {@Code Display.DEFAULT_DISPLAY} as the target display.
+ */
+ static ContextImpl createSystemUiContext(ContextImpl systemContext) {
+ return createSystemUiContext(systemContext, Display.DEFAULT_DISPLAY);
+ }
+
@UnsupportedAppUsage
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
return createAppContext(mainThread, packageInfo, null);
@@ -3209,13 +3206,7 @@
@Override
public IBinder getWindowContextToken() {
- switch (mContextType) {
- case CONTEXT_TYPE_WINDOW_CONTEXT:
- case CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI:
- return mToken;
- default:
- return null;
- }
+ return mContextType == CONTEXT_TYPE_WINDOW_CONTEXT ? mToken : null;
}
private void checkMode(int mode) {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 601ec9a..0210a94 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -341,7 +341,7 @@
@UnsupportedAppUsage
boolean switchUser(int userid);
@UnsupportedAppUsage
- void setStopBackgroundUsersOnSwitch(int value);
+ void setStopUserOnSwitch(int value);
boolean removeTask(int taskId);
@UnsupportedAppUsage
void registerProcessObserver(in IProcessObserver observer);
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 11c01e6..22b1c03 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -240,6 +240,14 @@
*/
public static final String EXTRA_NEW_WALLPAPER_ID = "android.service.wallpaper.extra.ID";
+ /**
+ * Extra passed on {@link Intent.ACTION_WALLPAPER_CHANGED} indicating if wallpaper was set from
+ * a foreground app.
+ * @hide
+ */
+ public static final String EXTRA_FROM_FOREGROUND_APP =
+ "android.service.wallpaper.extra.FROM_FOREGROUND_APP";
+
// flags for which kind of wallpaper to act on
/** @hide */
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index a0d2977..01875ed 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -16,6 +16,7 @@
package android.app.admin;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
@@ -77,6 +78,13 @@
OnCrossProfileWidgetProvidersChangeListener listener);
/**
+ * @param userHandle the handle of the user whose profile owner is being fetched.
+ * @return the configured supervision app if it exists and is the device owner or policy owner.
+ */
+ public abstract @Nullable ComponentName getProfileOwnerOrDeviceOwnerSupervisionComponent(
+ @NonNull UserHandle userHandle);
+
+ /**
* Checks if an app with given uid is an active device owner of its user.
*
* <p>This takes the DPMS lock. DO NOT call from PM/UM/AM with their lock held.
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index a02fdd3..b5298fc 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -995,10 +995,9 @@
* OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
* OVERRIDE_MIN_ASPECT_RATIO_LARGE
*
- * If OVERRIDE_MIN_ASPECT_RATIO is applied, and the activity's orientation is fixed to
- * portrait, the min aspect ratio given in the app's manifest will be overridden to the
- * largest enabled aspect ratio treatment unless the app's manifest value is higher.
- * TODO(b/203647190): add OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY instead of portrait by default
+ * If OVERRIDE_MIN_ASPECT_RATIO is applied, the min aspect ratio given in the app's manifest
+ * will be overridden to the largest enabled aspect ratio treatment unless the app's manifest
+ * value is higher.
* @hide
*/
@ChangeId
@@ -1008,6 +1007,19 @@
public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // buganizer id
/**
+ * This change id restricts treatments that force a given min aspect ratio to activities
+ * whose orientation is fixed to portrait.
+ *
+ * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S_V2)
+ @TestApi
+ public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // buganizer id
+
+ /**
* This change id sets the activity's min aspect ratio to a medium value as defined by
* OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE.
*
@@ -1336,9 +1348,7 @@
*/
@SizeChangesSupportMode
public int supportsSizeChanges() {
- if (CompatChanges.isChangeEnabled(FORCE_NON_RESIZE_APP,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ if (isChangeEnabled(FORCE_NON_RESIZE_APP)) {
return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
}
@@ -1346,9 +1356,7 @@
return SIZE_CHANGES_SUPPORTED_METADATA;
}
- if (CompatChanges.isChangeEnabled(FORCE_RESIZE_APP,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ if (isChangeEnabled(FORCE_RESIZE_APP)) {
return SIZE_CHANGES_SUPPORTED_OVERRIDE;
}
@@ -1360,9 +1368,7 @@
* @hide
*/
public boolean neverSandboxDisplayApis() {
- return CompatChanges.isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))
+ return isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS)
|| ConstrainDisplayApisConfig.neverConstrainDisplayApis(applicationInfo);
}
@@ -1371,9 +1377,7 @@
* @hide
*/
public boolean alwaysSandboxDisplayApis() {
- return CompatChanges.isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))
+ return isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS)
|| ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(applicationInfo);
}
@@ -1403,31 +1407,28 @@
* @hide
*/
public float getMinAspectRatio(@ScreenOrientation int orientation) {
- // TODO(b/203647190): check orientation only if OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY
- // In case the activity's orientation isn't fixed to portrait, OVERRIDE_MIN_ASPECT_RATIO
- // shouldn't be applied.
- if (applicationInfo == null || !CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))
- || !isFixedOrientationPortrait(orientation)) {
+ if (applicationInfo == null || !isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO) || (
+ isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
+ && !isFixedOrientationPortrait(orientation))) {
return mMinAspectRatio;
}
- if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE)) {
return Math.max(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, mMinAspectRatio);
}
- if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM)) {
return Math.max(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, mMinAspectRatio);
}
return mMinAspectRatio;
}
+ private boolean isChangeEnabled(long changeId) {
+ return CompatChanges.isChangeEnabled(changeId, applicationInfo.packageName,
+ UserHandle.getUserHandleForUid(applicationInfo.uid));
+ }
+
/** @hide */
public float getManifestMinAspectRatio() {
return mMinAspectRatio;
@@ -1495,9 +1496,7 @@
* @hide
*/
public boolean shouldCheckMinWidthHeightForMultiWindow() {
- return CompatChanges.isChangeEnabled(CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid));
+ return isChangeEnabled(CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW);
}
public void dump(Printer pw, String prefix) {
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index c8c122d..6b5bec9 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -408,6 +408,19 @@
}
/**
+ * Flag to decide if authentication should ignore enrollment state.
+ * Defaults to false (not ignoring enrollment state)
+ * @param ignoreEnrollmentState
+ * @return This builder.
+ * @hide
+ */
+ @NonNull
+ public Builder setIgnoreEnrollmentState(boolean ignoreEnrollmentState) {
+ mPromptInfo.setIgnoreEnrollmentState(ignoreEnrollmentState);
+ return this;
+ }
+
+ /**
* Creates a {@link BiometricPrompt}.
*
* @return An instance of {@link BiometricPrompt}.
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 339c654..e6b762a 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -45,6 +45,7 @@
private boolean mReceiveSystemEvents;
@NonNull private List<Integer> mAllowedSensorIds = new ArrayList<>();
private boolean mAllowBackgroundAuthentication;
+ private boolean mIgnoreEnrollmentState;
public PromptInfo() {
@@ -66,6 +67,7 @@
mReceiveSystemEvents = in.readBoolean();
mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader());
mAllowBackgroundAuthentication = in.readBoolean();
+ mIgnoreEnrollmentState = in.readBoolean();
}
public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -102,6 +104,7 @@
dest.writeBoolean(mReceiveSystemEvents);
dest.writeList(mAllowedSensorIds);
dest.writeBoolean(mAllowBackgroundAuthentication);
+ dest.writeBoolean(mIgnoreEnrollmentState);
}
public boolean containsTestConfigurations() {
@@ -192,6 +195,10 @@
mAllowBackgroundAuthentication = allow;
}
+ public void setIgnoreEnrollmentState(boolean ignoreEnrollmentState) {
+ mIgnoreEnrollmentState = ignoreEnrollmentState;
+ }
+
// Getters
public CharSequence getTitle() {
@@ -261,4 +268,8 @@
public boolean isAllowBackgroundAuthentication() {
return mAllowBackgroundAuthentication;
}
+
+ public boolean isIgnoreEnrollmentState() {
+ return mIgnoreEnrollmentState;
+ }
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index a3d595c..fe04e5d 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -531,7 +531,7 @@
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
- authenticate(crypto, cancel, callback, handler, mContext.getUserId());
+ authenticate(crypto, cancel, callback, handler, SENSOR_ID_ANY, mContext.getUserId(), flags);
}
/**
@@ -541,7 +541,7 @@
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
@NonNull AuthenticationCallback callback, Handler handler, int userId) {
- authenticate(crypto, cancel, callback, handler, SENSOR_ID_ANY, userId);
+ authenticate(crypto, cancel, callback, handler, SENSOR_ID_ANY, userId, 0 /* flags */);
}
/**
@@ -550,7 +550,8 @@
*/
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
- @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId) {
+ @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId,
+ int flags) {
FrameworkStatsLog.write(FrameworkStatsLog.AUTH_DEPRECATED_API_USED,
AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_AUTHENTICATE,
@@ -566,6 +567,8 @@
return;
}
+ final boolean ignoreEnrollmentState = flags == 0 ? false : true;
+
if (mService != null) {
try {
useHandler(handler);
@@ -573,7 +576,7 @@
mCryptoObject = crypto;
final long operationId = crypto != null ? crypto.getOpId() : 0;
final long authId = mService.authenticate(mToken, operationId, sensorId, userId,
- mServiceReceiver, mContext.getOpPackageName());
+ mServiceReceiver, mContext.getOpPackageName(), ignoreEnrollmentState);
if (cancel != null) {
cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index de94b2f..ba1dc6d 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -52,7 +52,8 @@
// permission. This is effectively deprecated, since it only comes through FingerprintManager
// now. A requestId is returned that can be used to cancel this operation.
long authenticate(IBinder token, long operationId, int sensorId, int userId,
- IFingerprintServiceReceiver receiver, String opPackageName);
+ IFingerprintServiceReceiver receiver, String opPackageName,
+ boolean shouldIgnoreEnrollmentState);
// Uses the fingerprint hardware to detect for the presence of a finger, without giving details
// about accept/reject/lockout. A requestId is returned that can be used to cancel this
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 2d263a5..c77399f 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1575,7 +1575,7 @@
void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages,
float xOffsetStep) {
// to save creating a runnable, check twice
- long current = SystemClock.elapsedRealtime();
+ long current = System.currentTimeMillis();
long lapsed = current - currentPage.getLastUpdateTime();
// Always update the page when the last update time is <= 0
// This is important especially when the device first boots
@@ -1768,6 +1768,7 @@
return;
}
}
+ processLocalColors(mPendingXOffset, mPendingYOffset);
}
/**
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl
index aca17e4..c7fd380 100644
--- a/core/java/android/view/IRecentsAnimationRunner.aidl
+++ b/core/java/android/view/IRecentsAnimationRunner.aidl
@@ -63,5 +63,5 @@
* Called when the task of an activity that has been started while the recents animation
* was running becomes ready for control.
*/
- void onTaskAppeared(in RemoteAnimationTarget app) = 3;
+ void onTasksAppeared(in RemoteAnimationTarget[] app) = 3;
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index ae32a48..9e41e4d 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -866,23 +866,6 @@
void attachWindowContextToWindowToken(IBinder clientToken, IBinder token);
/**
- * Attaches a {@code clientToken} to associate with DisplayContent.
- * <p>
- * Note that this API should be invoked after calling
- * {@link android.window.WindowTokenClient#attachContext(Context)}
- * </p>
- *
- * @param clientToken {@link android.window.WindowContext#getWindowContextToken()
- * the WindowContext's token}
- * @param displayId The display associated with the window context
- *
- * @return the DisplayContent's {@link android.app.res.Configuration} if the Context is
- * attached to the DisplayContent successfully. {@code null}, otherwise.
- * @throws android.view.WindowManager.InvalidDisplayException if the display ID is invalid
- */
- Configuration attachToDisplayContent(IBinder clientToken, int displayId);
-
- /**
* Detaches {@link android.window.WindowContext} from the window manager node it's currently
* attached to. It is no-op if the WindowContext is not attached to a window manager node.
*
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 51cd95e..8764ccc 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2441,6 +2441,20 @@
public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000;
/**
+ * Flag to indicate that this window will be excluded while computing the magnifiable region
+ * on the un-scaled screen coordinate, which could avoid the cutout on the magnification
+ * border. It should be used for unmagnifiable overlays.
+ *
+ * </p><p>
+ * Note unlike {@link #PRIVATE_FLAG_NOT_MAGNIFIABLE}, this flag doesn't affect the ability
+ * of magnification. If you want to the window to be unmagnifiable and doesn't lead to the
+ * cutout, you need to combine both of them.
+ * </p><p>
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION = 0x00200000;
+
+ /**
* Flag to prevent the window from being magnified by the accessibility magnifier.
*
* TODO(b/190623172): This is a temporary solution and need to find out another way instead.
@@ -2551,6 +2565,7 @@
PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
+ PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION,
PRIVATE_FLAG_NOT_MAGNIFIABLE,
PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION,
PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC,
@@ -2632,6 +2647,10 @@
equals = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
name = "IS_ROUNDED_CORNERS_OVERLAY"),
@ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION,
+ equals = PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION,
+ name = "EXCLUDE_FROM_SCREEN_MAGNIFICATION"),
+ @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_NOT_MAGNIFIABLE,
equals = PRIVATE_FLAG_NOT_MAGNIFIABLE,
name = "NOT_MAGNIFIABLE"),
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2357d13..f724285 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -5735,24 +5735,44 @@
return previousLayoutId == getLayoutId() && mViewId == overrideId;
}
- // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
- // should set it to false.
- private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
- ColorResources colorResources, boolean topLevel) {
-
+ /**
+ * Returns the RemoteViews that should be used in the reapply operation.
+ *
+ * If the current RemoteViews has multiple layout, this will select the correct one.
+ *
+ * @throws RuntimeException If the current RemoteViews should not be reapplied onto the provided
+ * View.
+ */
+ private RemoteViews getRemoteViewsToReapply(Context context, View v, @Nullable SizeF size) {
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
// In the case that a view has this RemoteViews applied in one orientation or size, is
// persisted across change, and has the RemoteViews re-applied in a different situation
// (orientation or size), we throw an exception, since the layouts may be completely
// unrelated.
- if (hasMultipleLayouts()) {
+ // If the ViewID has been changed on the view, or is changed by the RemoteViews, we also
+ // may throw an exception, as the RemoteViews will probably not apply properly.
+ // However, we need to let potentially unrelated RemoteViews apply, as this lack of testing
+ // is already used in production code in some apps.
+ if (hasMultipleLayouts()
+ || rvToApply.mViewId != View.NO_ID
+ || v.getTag(R.id.remote_views_override_id) != null) {
if (!rvToApply.canRecycleView(v)) {
throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
" that does not share the same root layout id.");
}
}
+ return rvToApply;
+ }
+
+ // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
+ // should set it to false.
+ private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
+ ColorResources colorResources, boolean topLevel) {
+
+ RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
+
rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
// If the parent of the view is has is a root, resolve the recycling.
@@ -5789,17 +5809,7 @@
public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
- RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
- // In the case that a view has this RemoteViews applied in one orientation, is persisted
- // across orientation change, and has the RemoteViews re-applied in the new orientation,
- // we throw an exception, since the layouts may be completely unrelated.
- if (hasMultipleLayouts()) {
- if (!rvToApply.canRecycleView(v)) {
- throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
- " that does not share the same root layout id.");
- }
- }
+ RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
context, listener, handler, colorResources, v, true /* topLevel */)
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 17b675f..5aa6233 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -19,10 +19,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
import android.view.IWindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
+import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
@@ -35,6 +38,7 @@
* @hide
*/
public class WindowContextController {
+ private final IWindowManager mWms;
/**
* {@code true} to indicate that the {@code mToken} is associated with a
* {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a
@@ -52,7 +56,14 @@
* {@link Context#getWindowContextToken()}.
*/
public WindowContextController(@NonNull WindowTokenClient token) {
+ this(token, WindowManagerGlobal.getWindowManagerService());
+ }
+
+ /** Used for test only. DO NOT USE it in production code. */
+ @VisibleForTesting
+ public WindowContextController(@NonNull WindowTokenClient token, IWindowManager mockWms) {
mToken = token;
+ mWms = mockWms;
}
/**
@@ -69,7 +80,19 @@
throw new IllegalStateException("A Window Context can be only attached to "
+ "a DisplayArea once.");
}
- mAttachedToDisplayArea = mToken.attachToDisplayArea(type, displayId, options);
+ try {
+ final Configuration configuration = mWms.attachWindowContextToDisplayArea(mToken, type,
+ displayId, options);
+ if (configuration != null) {
+ mAttachedToDisplayArea = true;
+ // Send the DisplayArea's configuration to WindowContext directly instead of
+ // waiting for dispatching from WMS.
+ mToken.onConfigurationChanged(configuration, displayId,
+ false /* shouldReportConfigChange */);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -97,14 +120,22 @@
throw new IllegalStateException("The Window Context should have been attached"
+ " to a DisplayArea.");
}
- mToken.attachToWindowToken(windowToken);
+ try {
+ mWms.attachWindowContextToWindowToken(mToken, windowToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** Detaches the window context from the node it's currently associated with. */
public void detachIfNeeded() {
if (mAttachedToDisplayArea) {
- mToken.detachFromWindowContainerIfNeeded();
- mAttachedToDisplayArea = false;
+ try {
+ mWms.detachWindowContextFromWindowContainer(mToken);
+ mAttachedToDisplayArea = false;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
}
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index b331a9e..f3e3859 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -21,7 +21,6 @@
import static android.window.ConfigurationHelper.shouldUpdateResources;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.IWindowToken;
import android.app.ResourcesManager;
@@ -32,11 +31,7 @@
import android.os.Bundle;
import android.os.Debug;
import android.os.IBinder;
-import android.os.RemoteException;
import android.util.Log;
-import android.view.IWindowManager;
-import android.view.WindowManager.LayoutParams.WindowType;
-import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
@@ -64,14 +59,10 @@
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
- private IWindowManager mWms;
-
private final Configuration mConfiguration = new Configuration();
private boolean mShouldDumpConfigForIme;
- private boolean mAttachToWindowContainer;
-
/**
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
* can only attach one {@link Context}.
@@ -93,88 +84,6 @@
}
/**
- * Attaches this {@link WindowTokenClient} to a {@link com.android.server.wm.DisplayArea}.
- *
- * @param type The window type of the {@link WindowContext}
- * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
- * @param options The window context launched option
- * @return {@code true} if attaching successfully.
- */
- public boolean attachToDisplayArea(@WindowType int type, int displayId,
- @Nullable Bundle options) {
- try {
- final Configuration configuration = getWindowManagerService()
- .attachWindowContextToDisplayArea(this, type, displayId, options);
- if (configuration == null) {
- return false;
- }
- onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
- mAttachToWindowContainer = true;
- return true;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Attaches this {@link WindowTokenClient} to a {@code DisplayContent}.
- *
- * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
- * @return {@code true} if attaching successfully.
- */
- public boolean attachToDisplayContent(int displayId) {
- final IWindowManager wms = getWindowManagerService();
- // #createSystemUiContext may call this method before WindowManagerService is initialized.
- if (wms == null) {
- return false;
- }
- try {
- final Configuration configuration = wms.attachToDisplayContent(this, displayId);
- if (configuration == null) {
- return false;
- }
- onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
- mAttachToWindowContainer = true;
- return true;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Attaches this {@link WindowTokenClient} to a {@code windowToken}.
- *
- * @param windowToken the window token to associated with
- */
- public void attachToWindowToken(IBinder windowToken) {
- try {
- getWindowManagerService().attachWindowContextToWindowToken(this, windowToken);
- mAttachToWindowContainer = true;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** Detaches this {@link WindowTokenClient} from associated WindowContainer if there's one. */
- public void detachFromWindowContainerIfNeeded() {
- if (!mAttachToWindowContainer) {
- return;
- }
- try {
- getWindowManagerService().detachWindowContextFromWindowContainer(this);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- private IWindowManager getWindowManagerService() {
- if (mWms == null) {
- mWms = WindowManagerGlobal.getWindowManagerService();
- }
- return mWms;
- }
-
- /**
* Called when {@link Configuration} updates from the server side receive.
*
* @param newConfig the updated {@link Configuration}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 32db186..90646a8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1930,8 +1930,9 @@
STREAM_MUSIC as if it's on TV platform. -->
<bool name="config_single_volume">false</bool>
- <!-- Flag indicating whether the volume panel should show remote sessions. -->
- <bool name="config_volumeShowRemoteSessions">true</bool>
+ <!-- Flag indicating whether platform level volume adjustments are enabled for remote sessions
+ on grouped devices. -->
+ <bool name="config_volumeAdjustmentForRemoteGroupSessions">true</bool>
<!-- Flag indicating that an outbound call must have a call capable phone account
that has declared it can process the call's handle. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 758990d..9bb92e6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4444,7 +4444,7 @@
<java-symbol type="dimen" name="config_wallpaperDimAmount" />
- <java-symbol type="bool" name="config_volumeShowRemoteSessions" />
+ <java-symbol type="bool" name="config_volumeAdjustmentForRemoteGroupSessions" />
<java-symbol type="integer" name="config_customizedMaxCachedProcesses" />
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index 52cb9f3..a6e351d 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -24,13 +24,16 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.content.res.Configuration;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
+import android.view.IWindowManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -56,14 +59,17 @@
public class WindowContextControllerTest {
private WindowContextController mController;
@Mock
+ private IWindowManager mMockWms;
+ @Mock
private WindowTokenClient mMockToken;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mController = new WindowContextController(mMockToken);
+ mController = new WindowContextController(mMockToken, mMockWms);
doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
- doReturn(true).when(mMockToken).attachToDisplayArea(anyInt(), anyInt(), any());
+ doReturn(new Configuration()).when(mMockWms).attachWindowContextToDisplayArea(any(),
+ anyInt(), anyInt(), any());
}
@Test(expected = IllegalStateException.class)
@@ -75,10 +81,10 @@
}
@Test
- public void testDetachIfNeeded_NotAttachedYet_DoNothing() {
+ public void testDetachIfNeeded_NotAttachedYet_DoNothing() throws Exception {
mController.detachIfNeeded();
- verify(mMockToken, never()).detachFromWindowContainerIfNeeded();
+ verify(mMockWms, never()).detachWindowContextFromWindowContainer(any());
}
@Test
@@ -87,6 +93,8 @@
null /* options */);
assertThat(mController.mAttachedToDisplayArea).isTrue();
+ verify(mMockToken).onConfigurationChanged(any(), eq(DEFAULT_DISPLAY),
+ eq(false) /* shouldReportConfigChange */);
mController.detachIfNeeded();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index 1e9fda6..44af1a9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -73,17 +73,55 @@
static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule;
final boolean shouldFinishPrimaryWithSecondary = (splitRule instanceof SplitPairRule)
- && ((SplitPairRule) splitRule).shouldFinishPrimaryWithSecondary();
+ && ((SplitPairRule) splitRule).getFinishPrimaryWithSecondary()
+ != SplitRule.FINISH_NEVER;
return shouldFinishPrimaryWithSecondary || isPlaceholderContainer;
}
static boolean shouldFinishSecondaryWithPrimary(@NonNull SplitRule splitRule) {
final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule;
final boolean shouldFinishSecondaryWithPrimary = (splitRule instanceof SplitPairRule)
- && ((SplitPairRule) splitRule).shouldFinishSecondaryWithPrimary();
+ && ((SplitPairRule) splitRule).getFinishSecondaryWithPrimary()
+ != SplitRule.FINISH_NEVER;
return shouldFinishSecondaryWithPrimary || isPlaceholderContainer;
}
+ static boolean shouldFinishAssociatedContainerWhenStacked(int finishBehavior) {
+ return finishBehavior == SplitRule.FINISH_ALWAYS;
+ }
+
+ static boolean shouldFinishAssociatedContainerWhenAdjacent(int finishBehavior) {
+ return finishBehavior == SplitRule.FINISH_ALWAYS
+ || finishBehavior == SplitRule.FINISH_ADJACENT;
+ }
+
+ static int getFinishPrimaryWithSecondaryBehavior(@NonNull SplitRule splitRule) {
+ if (splitRule instanceof SplitPlaceholderRule) {
+ return ((SplitPlaceholderRule) splitRule).getFinishPrimaryWithSecondary();
+ }
+ if (splitRule instanceof SplitPairRule) {
+ return ((SplitPairRule) splitRule).getFinishPrimaryWithSecondary();
+ }
+ return SplitRule.FINISH_NEVER;
+ }
+
+ static int getFinishSecondaryWithPrimaryBehavior(@NonNull SplitRule splitRule) {
+ if (splitRule instanceof SplitPlaceholderRule) {
+ return SplitRule.FINISH_ALWAYS;
+ }
+ if (splitRule instanceof SplitPairRule) {
+ return ((SplitPairRule) splitRule).getFinishSecondaryWithPrimary();
+ }
+ return SplitRule.FINISH_NEVER;
+ }
+
+ static boolean isStickyPlaceholderRule(@NonNull SplitRule splitRule) {
+ if (!(splitRule instanceof SplitPlaceholderRule)) {
+ return false;
+ }
+ return ((SplitPlaceholderRule) splitRule).isSticky();
+ }
+
@Override
public String toString() {
return "SplitContainer{"
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 9014102..68c1904 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -16,6 +16,12 @@
package androidx.window.extensions.embedding;
+import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
+import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
+import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
+import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenAdjacent;
+import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenStacked;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
@@ -460,6 +466,11 @@
return false;
}
+ if (isStickyPlaceholderRule(splitContainer.getSplitRule())) {
+ // The placeholder should remain after it was first shown.
+ return false;
+ }
+
if (mPresenter.shouldShowSideBySide(splitContainer)) {
return false;
}
@@ -643,6 +654,52 @@
return false;
}
+ /**
+ * Checks whether the associated container should be destroyed together with a finishing
+ * container. There is a case when primary containers for placeholders should be retained
+ * despite the rule configuration to finish primary with secondary - if they are marked as
+ * 'sticky' and the placeholder was finished when fully overlapping the primary container.
+ * @return {@code true} if the associated container should be retained (and not be finished).
+ */
+ boolean shouldRetainAssociatedContainer(@NonNull TaskFragmentContainer finishingContainer,
+ @NonNull TaskFragmentContainer associatedContainer) {
+ SplitContainer splitContainer = getActiveSplitForContainers(associatedContainer,
+ finishingContainer);
+ if (splitContainer == null) {
+ // Containers are not in the same split, no need to retain.
+ return false;
+ }
+ // Find the finish behavior for the associated container
+ int finishBehavior;
+ SplitRule splitRule = splitContainer.getSplitRule();
+ if (finishingContainer == splitContainer.getPrimaryContainer()) {
+ finishBehavior = getFinishSecondaryWithPrimaryBehavior(splitRule);
+ } else {
+ finishBehavior = getFinishPrimaryWithSecondaryBehavior(splitRule);
+ }
+ // Decide whether the associated container should be retained based on the current
+ // presentation mode.
+ if (mPresenter.shouldShowSideBySide(splitContainer)) {
+ return !shouldFinishAssociatedContainerWhenAdjacent(finishBehavior);
+ } else {
+ return !shouldFinishAssociatedContainerWhenStacked(finishBehavior);
+ }
+ }
+
+ /**
+ * @see #shouldRetainAssociatedContainer(TaskFragmentContainer, TaskFragmentContainer)
+ */
+ boolean shouldRetainAssociatedActivity(@NonNull TaskFragmentContainer finishingContainer,
+ @NonNull Activity associatedActivity) {
+ TaskFragmentContainer associatedContainer = getContainerWithActivity(
+ associatedActivity.getActivityToken());
+ if (associatedContainer == null) {
+ return false;
+ }
+
+ return shouldRetainAssociatedContainer(finishingContainer, associatedContainer);
+ }
+
private final class LifecycleCallbacks implements ActivityLifecycleCallbacks {
@Override
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 6805fde..a1a53bc 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -228,6 +228,9 @@
// Finish dependent containers
for (TaskFragmentContainer container : mContainersToFinishOnExit) {
+ if (controller.shouldRetainAssociatedContainer(this, container)) {
+ continue;
+ }
container.finish(true /* shouldFinishDependent */, presenter,
wct, controller);
}
@@ -235,6 +238,9 @@
// Finish associated activities
for (Activity activity : mActivitiesToFinishOnExit) {
+ if (controller.shouldRetainAssociatedActivity(this, activity)) {
+ continue;
+ }
activity.finish();
}
mActivitiesToFinishOnExit.clear();
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 830d13d..d6678bf 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
index 9fe0247..7dc2f31 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml
@@ -65,25 +65,28 @@
<LinearLayout
android:id="@+id/top_end_container"
android:layout_gravity="top|end"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
+
<ImageButton
android:id="@+id/settings"
android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size"
android:contentDescription="@string/pip_phone_settings"
+ android:layout_gravity="top|start"
android:gravity="center"
android:src="@drawable/pip_ic_settings"
android:background="?android:selectableItemBackgroundBorderless" />
<ImageButton
- android:id="@+id/dismiss"
+ android:id="@+id/enter_split"
android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size"
- android:contentDescription="@string/pip_phone_close"
+ android:layout_gravity="top|start"
android:gravity="center"
- android:src="@drawable/pip_ic_close_white"
+ android:contentDescription="@string/pip_phone_enter_split"
+ android:src="@drawable/pip_expand"
android:background="?android:selectableItemBackgroundBorderless" />
</LinearLayout>
@@ -97,4 +100,14 @@
android:padding="@dimen/pip_resize_handle_padding"
android:src="@drawable/pip_resize_handle"
android:background="?android:selectableItemBackgroundBorderless" />
+
+ <ImageButton
+ android:id="@+id/dismiss"
+ android:layout_width="@dimen/pip_action_size"
+ android:layout_height="@dimen/pip_action_size"
+ android:contentDescription="@string/pip_phone_close"
+ android:layout_gravity="top|end"
+ android:gravity="center"
+ android:src="@drawable/pip_ic_close_white"
+ android:background="?android:selectableItemBackgroundBorderless" />
</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 764854a..c88fc16 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -24,6 +24,9 @@
<!-- Label for PIP settings button [CHAR LIMIT=NONE]-->
<string name="pip_phone_settings">Settings</string>
+ <!-- Label for the PIP enter split button [CHAR LIMIT=NONE] -->
+ <string name="pip_phone_enter_split">Enter split screen</string>
+
<!-- Title of menu shown over picture-in-picture. Used for accessibility. -->
<string name="pip_menu_title">Menu</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index f7af4e1..caa5327 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1036,10 +1036,9 @@
// notification, so that the bubble will be re-created if shouldBubbleUp returns
// true.
mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
- } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble
- && !entry.getRanking().isSuspended()) {
+ } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
entry.setFlagBubble(true);
- onEntryUpdated(entry, true /* shouldBubbleUp */);
+ onEntryUpdated(entry, shouldBubbleUp && !entry.getRanking().isSuspended());
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index b80dcd0..711a0ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -43,6 +43,7 @@
import com.android.wm.shell.pip.tv.TvPipMenuController;
import com.android.wm.shell.pip.tv.TvPipNotificationController;
import com.android.wm.shell.pip.tv.TvPipTransition;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -160,13 +161,14 @@
PipTransitionController pipTransitionController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<LegacySplitScreenController> splitScreenOptional,
+ Optional<SplitScreenController> newSplitScreenOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
- shellTaskOrganizer, mainExecutor);
+ pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+ displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 944dfed..ec70147 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -55,6 +55,7 @@
import com.android.wm.shell.pip.phone.PipController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipTouchHandler;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
import com.android.wm.shell.transition.Transitions;
@@ -215,14 +216,15 @@
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipTransitionController pipTransitionController,
Optional<LegacySplitScreenController> splitScreenOptional,
+ Optional<SplitScreenController> newSplitScreenOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
- shellTaskOrganizer, mainExecutor);
+ pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+ displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b6e5804..6cc5f09 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -77,6 +77,7 @@
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
@@ -126,7 +127,8 @@
private final int mExitAnimationDuration;
private final int mCrossFadeAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
- private final Optional<LegacySplitScreenController> mSplitScreenOptional;
+ private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
+ private final Optional<SplitScreenController> mSplitScreenOptional;
protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor;
@@ -252,7 +254,8 @@
@NonNull PipAnimationController pipAnimationController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
@NonNull PipTransitionController pipTransitionController,
- Optional<LegacySplitScreenController> splitScreenOptional,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -274,6 +277,7 @@
mPipAnimationController = pipAnimationController;
mPipUiEventLoggerLogger = pipUiEventLogger;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+ mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer;
mMainExecutor = mainExecutor;
@@ -373,8 +377,11 @@
* activity render it's final configuration while the Task is still in PiP.
* - setWindowingMode to undefined at the end of transition
* @param animationDurationMs duration in millisecond for the exiting PiP transition
+ * @param requestEnterSplit whether the enterSplit button is pressed on PiP or not.
+ * Indicate the user wishes to directly put PiP into split screen
+ * mode.
*/
- public void exitPip(int animationDurationMs) {
+ public void exitPip(int animationDurationMs, boolean requestEnterSplit) {
if (!mPipTransitionState.isInPip()
|| mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP
|| mToken == null) {
@@ -387,7 +394,7 @@
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
- final int direction = syncWithSplitScreenBounds(destinationBounds)
+ final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
: TRANSITION_DIRECTION_LEAVE_PIP;
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
@@ -396,7 +403,7 @@
// We set to fullscreen here for now, but later it will be set to UNDEFINED for
// the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
wct.setActivityWindowingMode(mToken,
- direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
+ direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit
? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_FULLSCREEN);
wct.setBounds(mToken, destinationBounds);
@@ -435,7 +442,7 @@
wct.setWindowingMode(mToken, getOutPipWindowingMode());
// Simply reset the activity mode set prior to the animation running.
wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
- mSplitScreenOptional.ifPresent(splitScreen -> {
+ mLegacySplitScreenOptional.ifPresent(splitScreen -> {
if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
wct.reparent(mToken, splitScreen.getSecondaryRoot(), true /* onTop */);
}
@@ -1165,6 +1172,7 @@
@PipAnimationController.TransitionDirection int direction,
@PipAnimationController.AnimationType int type) {
final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds());
+ final boolean isPipTopLeft = isPipTopLeft();
mPipBoundsState.setBounds(destinationBounds);
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
removePipImmediately();
@@ -1210,10 +1218,10 @@
null /* callback */, false /* withStartDelay */);
});
} else {
- applyFinishBoundsResize(wct, direction);
+ applyFinishBoundsResize(wct, direction, isPipTopLeft);
}
} else {
- applyFinishBoundsResize(wct, direction);
+ applyFinishBoundsResize(wct, direction, isPipTopLeft);
}
finishResizeForMenu(destinationBounds);
@@ -1241,7 +1249,11 @@
} else if (isOutPipDirection(direction)) {
// If we are animating to fullscreen or split screen, then we need to reset the
// override bounds on the task to ensure that the task "matches" the parent's bounds.
- taskBounds = null;
+ if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+ taskBounds = destinationBounds;
+ } else {
+ taskBounds = null;
+ }
applyWindowingModeChangeOnExit(wct, direction);
} else {
// Just a resize in PIP
@@ -1261,8 +1273,20 @@
* applying it.
*/
public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct,
- @PipAnimationController.TransitionDirection int direction) {
- mTaskOrganizer.applyTransaction(wct);
+ @PipAnimationController.TransitionDirection int direction, boolean wasPipTopLeft) {
+ if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+ mSplitScreenOptional.get().enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct);
+ } else {
+ mTaskOrganizer.applyTransaction(wct);
+ }
+ }
+
+ private boolean isPipTopLeft() {
+ final Rect topLeft = new Rect();
+ final Rect bottomRight = new Rect();
+ mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
+
+ return topLeft.contains(mPipBoundsState.getBounds());
}
/**
@@ -1347,18 +1371,27 @@
}
/**
- * Sync with {@link LegacySplitScreenController} on destination bounds if PiP is going to split
- * screen.
+ * Sync with {@link LegacySplitScreenController} or {@link SplitScreenController} on destination
+ * bounds if PiP is going to split screen.
*
* @param destinationBoundsOut contain the updated destination bounds if applicable
* @return {@code true} if destinationBounds is altered for split screen
*/
- private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut) {
- if (!mSplitScreenOptional.isPresent()) {
+ private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut, boolean enterSplit) {
+ if (enterSplit && mSplitScreenOptional.isPresent()) {
+ final Rect topLeft = new Rect();
+ final Rect bottomRight = new Rect();
+ mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
+ final boolean isPipTopLeft = isPipTopLeft();
+ destinationBoundsOut.set(isPipTopLeft ? topLeft : bottomRight);
+ return true;
+ }
+
+ if (!mLegacySplitScreenOptional.isPresent()) {
return false;
}
- LegacySplitScreenController legacySplitScreen = mSplitScreenOptional.get();
+ LegacySplitScreenController legacySplitScreen = mLegacySplitScreenOptional.get();
if (!legacySplitScreen.isDividerVisible()) {
// fail early if system is not in split screen mode
return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index ae8c1b6..5687f4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -95,6 +95,11 @@
* Called when the PIP requested to show the menu.
*/
void onPipShowMenu();
+
+ /**
+ * Called when the PIP requested to enter Split.
+ */
+ void onEnterSplit();
}
private final Matrix mMoveTransform = new Matrix();
@@ -458,6 +463,10 @@
mListeners.forEach(Listener::onPipDismiss);
}
+ void onEnterSplit() {
+ mListeners.forEach(Listener::onEnterSplit);
+ }
+
/**
* @return the best set of actions to show in the PiP menu.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index 47a8c67..69ae45d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -151,7 +151,7 @@
result = true;
break;
case AccessibilityNodeInfo.ACTION_EXPAND:
- mMotionHelper.expandLeavePip();
+ mMotionHelper.expandLeavePip(false /* skipAnimation */);
result = true;
break;
default:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 8c431f0..10bc7e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -482,7 +482,8 @@
false /* fromShelfAdjustment */,
wct /* windowContainerTransaction */);
if (wct != null) {
- mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME);
+ mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME,
+ false /* wasPipTopLeft */);
}
};
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
index 3eeba6e..0644657 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
@@ -18,8 +18,6 @@
import android.content.Context;
import android.graphics.Rect;
-import android.util.Log;
-import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -34,6 +32,7 @@
protected ViewGroup mViewRoot;
protected ViewGroup mTopEndContainer;
protected View mDragHandle;
+ protected View mEnterSplitButton;
protected View mSettingsButton;
protected View mDismissButton;
@@ -44,14 +43,13 @@
* Bind the necessary views.
*/
public void bindViews(ViewGroup viewRoot, ViewGroup topEndContainer, View dragHandle,
- View settingsButton, View dismissButton) {
+ View enterSplitButton, View settingsButton, View dismissButton) {
mViewRoot = viewRoot;
mTopEndContainer = topEndContainer;
mDragHandle = dragHandle;
+ mEnterSplitButton = enterSplitButton;
mSettingsButton = settingsButton;
mDismissButton = dismissButton;
-
- bindInitialViewState();
}
/**
@@ -72,22 +70,4 @@
v.setLayoutParams(params);
}
}
-
- /** Calculate the initial state of the menu icons. Called when the menu is first created. */
- private void bindInitialViewState() {
- if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
- || mSettingsButton == null || mDismissButton == null) {
- Log.e(TAG, "One of the required views is null.");
- return;
- }
- // The menu view layout starts out with the settings button aligned at the top|end of the
- // view group next to the dismiss button. On phones, the settings button should be aligned
- // to the top|start of the view, so move it to parent view group to then align it to the
- // top|start of the menu.
- mTopEndContainer.removeView(mSettingsButton);
- mViewRoot.addView(mSettingsButton);
-
- setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
- setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 8ef2b6b..7bbebe5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -99,7 +99,7 @@
private static final float MENU_BACKGROUND_ALPHA = 0.3f;
private static final float DISABLED_ACTION_ALPHA = 0.54f;
- private static final boolean ENABLE_RESIZE_HANDLE = false;
+ private static final boolean ENABLE_ENTER_SPLIT = false;
private int mMenuState;
private boolean mAllowMenuTimeout = true;
@@ -139,7 +139,7 @@
protected View mViewRoot;
protected View mSettingsButton;
protected View mDismissButton;
- protected View mResizeHandle;
+ protected View mEnterSplitButton;
protected View mTopEndContainer;
protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
@@ -177,14 +177,23 @@
}
});
- mResizeHandle = findViewById(R.id.resize_handle);
- mResizeHandle.setAlpha(0);
+ mEnterSplitButton = findViewById(R.id.enter_split);
+ mEnterSplitButton.setAlpha(0);
+ mEnterSplitButton.setOnClickListener(v -> {
+ if (mMenuContainer.getAlpha() != 0) {
+ enterSplit();
+ }
+ });
+
+ findViewById(R.id.resize_handle).setAlpha(0);
+
mActionsGroup = findViewById(R.id.actions_group);
mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
R.dimen.pip_between_action_padding_land);
mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext);
mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
- mResizeHandle, mSettingsButton, mDismissButton);
+ findViewById(R.id.resize_handle), mEnterSplitButton, mSettingsButton,
+ mDismissButton);
mDismissFadeOutDurationMs = context.getResources()
.getInteger(R.integer.config_pipExitAnimationDuration);
@@ -268,14 +277,13 @@
mSettingsButton.getAlpha(), 1f);
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 1f);
- ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
- mResizeHandle.getAlpha(),
- ENABLE_RESIZE_HANDLE && showResizeHandle ? 1f : 0f);
+ ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
+ mEnterSplitButton.getAlpha(), ENABLE_ENTER_SPLIT ? 1f : 0f);
if (menuState == MENU_STATE_FULL) {
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
- resizeAnim);
+ enterSplitAnim);
} else {
- mMenuContainerAnimator.playTogether(resizeAnim);
+ mMenuContainerAnimator.playTogether(enterSplitAnim);
}
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS);
@@ -328,7 +336,7 @@
mMenuContainer.setAlpha(0f);
mSettingsButton.setAlpha(0f);
mDismissButton.setAlpha(0f);
- mResizeHandle.setAlpha(0f);
+ mEnterSplitButton.setAlpha(0f);
}
void pokeMenu() {
@@ -368,9 +376,10 @@
mSettingsButton.getAlpha(), 0f);
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 0f);
- ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
- mResizeHandle.getAlpha(), 0f);
- mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, resizeAnim);
+ ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
+ mEnterSplitButton.getAlpha(), 0f);
+ mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
+ enterSplitAnim);
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
mMenuContainerAnimator.setDuration(getFadeOutDuration(animationType));
mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
@@ -522,6 +531,14 @@
}
}
+ private void enterSplit() {
+ // Do not notify menu visibility when hiding the menu, the controller will do this when it
+ // handles the message
+ hideMenu(mController::onEnterSplit, false /* notifyMenuVisibility */, true /* resize */,
+ ANIM_TYPE_HIDE);
+ }
+
+
private void showSettings() {
final Pair<ComponentName, Integer> topPipActivityInfo =
PipUtils.getTopPipActivity(mContext);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index dbd09fd..c634b7f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -338,22 +338,29 @@
* Resizes the pinned stack back to unknown windowing mode, which could be freeform or
* * fullscreen depending on the display area's windowing mode.
*/
- void expandLeavePip() {
- expandLeavePip(false /* skipAnimation */);
+ void expandLeavePip(boolean skipAnimation) {
+ expandLeavePip(skipAnimation, false /* enterSplit */);
+ }
+
+ /**
+ * Resizes the pinned task to split-screen mode.
+ */
+ void expandIntoSplit() {
+ expandLeavePip(false, true /* enterSplit */);
}
/**
* Resizes the pinned stack back to unknown windowing mode, which could be freeform or
* fullscreen depending on the display area's windowing mode.
*/
- void expandLeavePip(boolean skipAnimation) {
+ private void expandLeavePip(boolean skipAnimation, boolean enterSplit) {
if (DEBUG) {
Log.d(TAG, "exitPip: skipAnimation=" + skipAnimation
+ " callers=\n" + Debug.getCallers(5, " "));
}
cancelPhysicsAnimation();
mMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */);
- mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION);
+ mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION, enterSplit);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 9f2f6a5..570fd5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -139,7 +139,12 @@
@Override
public void onPipExpand() {
- mMotionHelper.expandLeavePip();
+ mMotionHelper.expandLeavePip(false /* skipAnimation */);
+ }
+
+ @Override
+ public void onEnterSplit() {
+ mMotionHelper.expandIntoSplit();
}
@Override
@@ -899,7 +904,7 @@
// Expand to fullscreen if this is a double tap
// the PiP should be frozen until the transition ends
setTouchEnabled(false);
- mMotionHelper.expandLeavePip();
+ mMotionHelper.expandLeavePip(false /* skipAnimation */);
}
} else if (mMenuState != MENU_STATE_FULL) {
if (mPipBoundsState.isStashed()) {
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 a2e9b64..00083d9 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
@@ -219,7 +219,7 @@
public void movePipToFullscreen() {
if (DEBUG) Log.d(TAG, "movePipToFullscreen(), state=" + stateToName(mState));
- mPipTaskOrganizer.exitPip(mResizeAnimationDuration);
+ mPipTaskOrganizer.exitPip(mResizeAnimationDuration, false /* requestEnterSplit */);
onPipDisappeared();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 836a6f6..7cf3baf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -41,10 +41,13 @@
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
+import com.android.wm.shell.util.StagedSplitBounds;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Manages the recent task list from the system, caching it as necessary.
@@ -62,6 +65,13 @@
// Mapping of split task ids, mappings are symmetrical (ie. if t1 is the taskid of a task in a
// pair, then mSplitTasks[t1] = t2, and mSplitTasks[t2] = t1)
private final SparseIntArray mSplitTasks = new SparseIntArray();
+ /**
+ * Maps taskId to {@link StagedSplitBounds} for both taskIDs.
+ * Meaning there will be two taskId integers mapping to the same object.
+ * If there's any ordering to the pairing than we can probably just get away with only one
+ * taskID mapping to it, leaving both for consistency with {@link #mSplitTasks} for now.
+ */
+ private final Map<Integer, StagedSplitBounds> mTaskSplitBoundsMap = new HashMap<>();
/**
* Creates {@link RecentTasksController}, returns {@code null} if the feature is not
@@ -97,15 +107,20 @@
/**
* Adds a split pair. This call does not validate the taskIds, only that they are not the same.
*/
- public void addSplitPair(int taskId1, int taskId2) {
+ public void addSplitPair(int taskId1, int taskId2, StagedSplitBounds splitBounds) {
if (taskId1 == taskId2) {
return;
}
// Remove any previous pairs
removeSplitPair(taskId1);
removeSplitPair(taskId2);
+ mTaskSplitBoundsMap.remove(taskId1);
+ mTaskSplitBoundsMap.remove(taskId2);
+
mSplitTasks.put(taskId1, taskId2);
mSplitTasks.put(taskId2, taskId1);
+ mTaskSplitBoundsMap.put(taskId1, splitBounds);
+ mTaskSplitBoundsMap.put(taskId2, splitBounds);
}
/**
@@ -116,6 +131,8 @@
if (pairedTaskId != INVALID_TASK_ID) {
mSplitTasks.delete(taskId);
mSplitTasks.delete(pairedTaskId);
+ mTaskSplitBoundsMap.remove(taskId);
+ mTaskSplitBoundsMap.remove(pairedTaskId);
}
}
@@ -203,7 +220,8 @@
if (pairedTaskId != INVALID_TASK_ID) {
final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
rawMapping.remove(pairedTaskId);
- recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo));
+ recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo,
+ mTaskSplitBoundsMap.get(pairedTaskId)));
} else {
recentTasks.add(new GroupedRecentTaskInfo(taskInfo));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 04058ed..7457be2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -202,11 +202,25 @@
return moveToSideStage(task, sideStagePosition);
}
+ public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition,
+ WindowContainerTransaction wct) {
+ final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
+ if (task == null) {
+ throw new IllegalArgumentException("Unknown taskId" + taskId);
+ }
+ return moveToSideStage(task, sideStagePosition, wct);
+ }
+
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitPosition int sideStagePosition) {
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
}
+ public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+ @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
+ return mStageCoordinator.moveToSideStage(task, sideStagePosition, wct);
+ }
+
public boolean removeFromSideStage(int taskId) {
return mStageCoordinator.removeFromSideStage(taskId);
}
@@ -224,6 +238,11 @@
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
}
+ public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
+ moveToSideStage(taskId,
+ leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+ }
+
public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 8471e1e..95886c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -96,6 +96,7 @@
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.StagedSplitBounds;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -280,6 +281,11 @@
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitPosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ return moveToSideStage(task, sideStagePosition, wct);
+ }
+
+ boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+ @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
setSideStagePosition(sideStagePosition, wct);
mSideStage.evictAllChildren(evictWct);
@@ -691,11 +697,25 @@
}
mRecentTasks.ifPresent(recentTasks -> {
+ Rect topLeftBounds = mSplitLayout.getBounds1();
+ Rect bottomRightBounds = mSplitLayout.getBounds2();
int mainStageTopTaskId = mMainStage.getTopVisibleChildTaskId();
int sideStageTopTaskId = mSideStage.getTopVisibleChildTaskId();
+ boolean sideStageTopLeft = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
+ int leftTopTaskId;
+ int rightBottomTaskId;
+ if (sideStageTopLeft) {
+ leftTopTaskId = sideStageTopTaskId;
+ rightBottomTaskId = mainStageTopTaskId;
+ } else {
+ leftTopTaskId = mainStageTopTaskId;
+ rightBottomTaskId = sideStageTopTaskId;
+ }
+ StagedSplitBounds splitBounds = new StagedSplitBounds(topLeftBounds, bottomRightBounds,
+ leftTopTaskId, rightBottomTaskId);
if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
// Update the pair for the top tasks
- recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId);
+ recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds);
}
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
index 0331ba1..603d05d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
@@ -30,25 +30,34 @@
public class GroupedRecentTaskInfo implements Parcelable {
public @NonNull ActivityManager.RecentTaskInfo mTaskInfo1;
public @Nullable ActivityManager.RecentTaskInfo mTaskInfo2;
+ public @Nullable StagedSplitBounds mStagedSplitBounds;
public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1) {
- this(task1, null);
+ this(task1, null, null);
}
public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1,
- @Nullable ActivityManager.RecentTaskInfo task2) {
+ @Nullable ActivityManager.RecentTaskInfo task2,
+ @Nullable StagedSplitBounds stagedSplitBounds) {
mTaskInfo1 = task1;
mTaskInfo2 = task2;
+ mStagedSplitBounds = stagedSplitBounds;
}
GroupedRecentTaskInfo(Parcel parcel) {
mTaskInfo1 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
mTaskInfo2 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
+ mStagedSplitBounds = parcel.readTypedObject(StagedSplitBounds.CREATOR);
}
@Override
public String toString() {
- return "Task1: " + getTaskInfo(mTaskInfo1) + ", Task2: " + getTaskInfo(mTaskInfo2);
+ String taskString = "Task1: " + getTaskInfo(mTaskInfo1)
+ + ", Task2: " + getTaskInfo(mTaskInfo2);
+ if (mStagedSplitBounds != null) {
+ taskString += ", SplitBounds: " + mStagedSplitBounds.toString();
+ }
+ return taskString;
}
private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) {
@@ -67,6 +76,7 @@
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeTypedObject(mTaskInfo1, flags);
parcel.writeTypedObject(mTaskInfo2, flags);
+ parcel.writeTypedObject(mStagedSplitBounds, flags);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
new file mode 100644
index 0000000..aadf792
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.util;
+
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container of various information needed to display split screen
+ * tasks/leashes/etc in Launcher
+ */
+public class StagedSplitBounds implements Parcelable {
+ public final Rect leftTopBounds;
+ public final Rect rightBottomBounds;
+ /** This rect represents the actual gap between the two apps */
+ public final Rect visualDividerBounds;
+ // This class is orientation-agnostic, so we compute both for later use
+ public final float topTaskPercent;
+ public final float leftTaskPercent;
+ /**
+ * If {@code true}, that means at the time of creation of this object, the
+ * split-screened apps were vertically stacked. This is useful in scenarios like
+ * rotation where the bounds won't change, but this variable can indicate what orientation
+ * the bounds were originally in
+ */
+ public final boolean appsStackedVertically;
+ public final int leftTopTaskId;
+ public final int rightBottomTaskId;
+
+ public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds,
+ int leftTopTaskId, int rightBottomTaskId) {
+ this.leftTopBounds = leftTopBounds;
+ this.rightBottomBounds = rightBottomBounds;
+ this.leftTopTaskId = leftTopTaskId;
+ this.rightBottomTaskId = rightBottomTaskId;
+
+ if (rightBottomBounds.top > leftTopBounds.top) {
+ // vertical apps, horizontal divider
+ this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom,
+ leftTopBounds.right, rightBottomBounds.top);
+ appsStackedVertically = true;
+ } else {
+ // horizontal apps, vertical divider
+ this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top,
+ rightBottomBounds.left, leftTopBounds.bottom);
+ appsStackedVertically = false;
+ }
+
+ leftTaskPercent = this.leftTopBounds.width() / (float) rightBottomBounds.right;
+ topTaskPercent = this.leftTopBounds.height() / (float) rightBottomBounds.bottom;
+ }
+
+ public StagedSplitBounds(Parcel parcel) {
+ leftTopBounds = parcel.readTypedObject(Rect.CREATOR);
+ rightBottomBounds = parcel.readTypedObject(Rect.CREATOR);
+ visualDividerBounds = parcel.readTypedObject(Rect.CREATOR);
+ topTaskPercent = parcel.readFloat();
+ leftTaskPercent = parcel.readFloat();
+ appsStackedVertically = parcel.readBoolean();
+ leftTopTaskId = parcel.readInt();
+ rightBottomTaskId = parcel.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeTypedObject(leftTopBounds, flags);
+ parcel.writeTypedObject(rightBottomBounds, flags);
+ parcel.writeTypedObject(visualDividerBounds, flags);
+ parcel.writeFloat(topTaskPercent);
+ parcel.writeFloat(leftTaskPercent);
+ parcel.writeBoolean(appsStackedVertically);
+ parcel.writeInt(leftTopTaskId);
+ parcel.writeInt(rightBottomTaskId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
+ + "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId + "\n"
+ + "Divider: " + visualDividerBounds + "\n"
+ + "AppsVertical? " + appsStackedVertically;
+ }
+
+ public static final Creator<StagedSplitBounds> CREATOR = new Creator<StagedSplitBounds>() {
+ @Override
+ public StagedSplitBounds createFromParcel(Parcel in) {
+ return new StagedSplitBounds(in);
+ }
+
+ @Override
+ public StagedSplitBounds[] newArray(int size) {
+ return new StagedSplitBounds[size];
+ }
+ };
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 0270093..0172cf32 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -50,6 +50,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before;
import org.junit.Test;
@@ -75,7 +76,8 @@
@Mock private PipTransitionController mMockPipTransitionController;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
- @Mock private Optional<LegacySplitScreenController> mMockOptionalSplitScreen;
+ @Mock private Optional<LegacySplitScreenController> mMockOptionalLegacySplitScreen;
+ @Mock private Optional<SplitScreenController> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState;
@@ -99,8 +101,9 @@
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
mPipBoundsAlgorithm, mMockPhonePipMenuController,
mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
- mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController,
- mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor));
+ mMockPipTransitionController, mMockOptionalLegacySplitScreen,
+ mMockOptionalSplitScreen, mMockDisplayController, mMockPipUiEventLogger,
+ mMockShellTaskOrganizer, mMainExecutor));
mMainExecutor.flushAll();
preparePipTaskOrg();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index a1e1231..19a5417 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -20,6 +20,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
@@ -31,10 +33,9 @@
import static java.lang.Integer.MAX_VALUE;
import android.app.ActivityManager;
-import android.app.WindowConfiguration;
import android.content.Context;
+import android.graphics.Rect;
import android.view.SurfaceControl;
-import android.window.TaskAppearedInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -45,6 +46,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
+import com.android.wm.shell.util.StagedSplitBounds;
import org.junit.Before;
import org.junit.Test;
@@ -106,8 +108,11 @@
setRawList(t1, t2, t3, t4, t5, t6);
// Mark a couple pairs [t2, t4], [t3, t5]
- mRecentTasksController.addSplitPair(t2.taskId, t4.taskId);
- mRecentTasksController.addSplitPair(t3.taskId, t5.taskId);
+ StagedSplitBounds pair1Bounds = new StagedSplitBounds(new Rect(), new Rect(), 2, 4);
+ StagedSplitBounds pair2Bounds = new StagedSplitBounds(new Rect(), new Rect(), 3, 5);
+
+ mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
+ mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
@@ -126,7 +131,8 @@
setRawList(t1, t2, t3);
// Add a pair
- mRecentTasksController.addSplitPair(t2.taskId, t3.taskId);
+ StagedSplitBounds pair1Bounds = new StagedSplitBounds(new Rect(), new Rect(), 2, 3);
+ mRecentTasksController.addSplitPair(t2.taskId, t3.taskId, pair1Bounds);
reset(mRecentTasksController);
// Remove one of the tasks and ensure the pair is removed
@@ -201,10 +207,23 @@
int[] flattenedTaskIds = new int[recentTasks.size() * 2];
for (int i = 0; i < recentTasks.size(); i++) {
GroupedRecentTaskInfo pair = recentTasks.get(i);
- flattenedTaskIds[2 * i] = pair.mTaskInfo1.taskId;
+ int taskId1 = pair.mTaskInfo1.taskId;
+ flattenedTaskIds[2 * i] = taskId1;
flattenedTaskIds[2 * i + 1] = pair.mTaskInfo2 != null
? pair.mTaskInfo2.taskId
: -1;
+
+ if (pair.mTaskInfo2 != null) {
+ assertNotNull(pair.mStagedSplitBounds);
+ int leftTopTaskId = pair.mStagedSplitBounds.leftTopTaskId;
+ int bottomRightTaskId = pair.mStagedSplitBounds.rightBottomTaskId;
+ // Unclear if pairs are ordered by split position, most likely not.
+ assertTrue(leftTopTaskId == taskId1 || leftTopTaskId == pair.mTaskInfo2.taskId);
+ assertTrue(bottomRightTaskId == taskId1
+ || bottomRightTaskId == pair.mTaskInfo2.taskId);
+ } else {
+ assertNull(pair.mStagedSplitBounds);
+ }
}
assertTrue("Expected: " + Arrays.toString(expectedTaskIds)
+ " Received: " + Arrays.toString(flattenedTaskIds),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/StagedSplitBoundsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/StagedSplitBoundsTest.java
new file mode 100644
index 0000000..ad73c56
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/StagedSplitBoundsTest.java
@@ -0,0 +1,94 @@
+package com.android.wm.shell.recents;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Rect;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.wm.shell.util.StagedSplitBounds;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class StagedSplitBoundsTest {
+ private static final int DEVICE_WIDTH = 100;
+ private static final int DEVICE_LENGTH = 200;
+ private static final int DIVIDER_SIZE = 20;
+ private static final int TASK_ID_1 = 4;
+ private static final int TASK_ID_2 = 9;
+
+ // Bounds in screen space
+ private final Rect mTopRect = new Rect();
+ private final Rect mBottomRect = new Rect();
+ private final Rect mLeftRect = new Rect();
+ private final Rect mRightRect = new Rect();
+
+ @Before
+ public void setup() {
+ mTopRect.set(0, 0, DEVICE_WIDTH, DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2);
+ mBottomRect.set(0, DEVICE_LENGTH / 2 + DIVIDER_SIZE / 2,
+ DEVICE_WIDTH, DEVICE_LENGTH);
+ mLeftRect.set(0, 0, DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2, DEVICE_LENGTH);
+ mRightRect.set(DEVICE_WIDTH / 2 + DIVIDER_SIZE / 2, 0,
+ DEVICE_WIDTH, DEVICE_LENGTH);
+ }
+
+ @Test
+ public void testVerticalStacked() {
+ StagedSplitBounds ssb = new StagedSplitBounds(mTopRect, mBottomRect,
+ TASK_ID_1, TASK_ID_2);
+ assertTrue(ssb.appsStackedVertically);
+ }
+
+ @Test
+ public void testHorizontalStacked() {
+ StagedSplitBounds ssb = new StagedSplitBounds(mLeftRect, mRightRect,
+ TASK_ID_1, TASK_ID_2);
+ assertFalse(ssb.appsStackedVertically);
+ }
+
+ @Test
+ public void testHorizontalDividerBounds() {
+ StagedSplitBounds ssb = new StagedSplitBounds(mTopRect, mBottomRect,
+ TASK_ID_1, TASK_ID_2);
+ Rect dividerBounds = ssb.visualDividerBounds;
+ assertEquals(0, dividerBounds.left);
+ assertEquals(DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2, dividerBounds.top);
+ assertEquals(DEVICE_WIDTH, dividerBounds.right);
+ assertEquals(DEVICE_LENGTH / 2 + DIVIDER_SIZE / 2, dividerBounds.bottom);
+ }
+
+ @Test
+ public void testVerticalDividerBounds() {
+ StagedSplitBounds ssb = new StagedSplitBounds(mLeftRect, mRightRect,
+ TASK_ID_1, TASK_ID_2);
+ Rect dividerBounds = ssb.visualDividerBounds;
+ assertEquals(DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2, dividerBounds.left);
+ assertEquals(0, dividerBounds.top);
+ assertEquals(DEVICE_WIDTH / 2 + DIVIDER_SIZE / 2, dividerBounds.right);
+ assertEquals(DEVICE_LENGTH, dividerBounds.bottom);
+ }
+
+ @Test
+ public void testEqualVerticalTaskPercent() {
+ StagedSplitBounds ssb = new StagedSplitBounds(mTopRect, mBottomRect,
+ TASK_ID_1, TASK_ID_2);
+ float topPercentSpaceTaken = (float) (DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2) / DEVICE_LENGTH;
+ assertEquals(topPercentSpaceTaken, ssb.topTaskPercent, 0.01);
+ }
+
+ @Test
+ public void testEqualHorizontalTaskPercent() {
+ StagedSplitBounds ssb = new StagedSplitBounds(mLeftRect, mRightRect,
+ TASK_ID_1, TASK_ID_2);
+ float leftPercentSpaceTaken = (float) (DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2) / DEVICE_WIDTH;
+ assertEquals(leftPercentSpaceTaken, ssb.leftTaskPercent, 0.01);
+ }
+}
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
index 9bbd0a9..29ef2b8 100644
--- a/libs/hwui/pipeline/skia/FunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -34,6 +34,8 @@
*/
class FunctorDrawable : public SkDrawable {
public:
+ constexpr static const char* const TYPE_NAME = "FunctorDrawable";
+
FunctorDrawable(int functor, SkCanvas* canvas)
: mBounds(canvas->getLocalClipBounds())
, mWebViewHandle(WebViewFunctorManager::instance().handleFor(functor)) {}
@@ -48,6 +50,8 @@
mWebViewHandle->onRemovedFromTree();
}
+ const char* getTypeName() const override { return TYPE_NAME; }
+
protected:
virtual SkRect onGetBounds() override { return mBounds; }
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
index 6777c00..41e3687 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.cpp
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
#include "TransformCanvas.h"
+
+#include "FunctorDrawable.h"
#include "HolePunch.h"
#include "SkData.h"
#include "SkDrawable.h"
@@ -35,7 +37,17 @@
}
void TransformCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) {
- drawable->draw(this, matrix);
+ // TransformCanvas filters all drawing commands while maintaining the current
+ // clip stack and transformation. We need to draw most SkDrawables, since their
+ // draw calls may call methods that affect the clip stack and transformation. (Any
+ // actual draw commands will then be filtered out.) But FunctorDrawables are used
+ // as leaf nodes which issue self-contained OpenGL/Vulkan commands. These won't
+ // affect the clip stack + transformation, and in some cases cause problems (e.g. if
+ // the surface only has an alpha channel). See b/203960959
+ const auto* drawableName = drawable->getTypeName();
+ if (drawableName == nullptr || strcmp(drawableName, FunctorDrawable::TYPE_NAME) != 0) {
+ drawable->draw(this, matrix);
+ }
}
bool TransformCanvas::onFilter(SkPaint& paint) const {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 3c43f4a6..a383c1e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -70,6 +70,7 @@
MediaRouter2Manager mRouterManager;
@VisibleForTesting
String mPackageName;
+ private final boolean mVolumeAdjustmentForRemoteGroupSessions;
private MediaDevice mCurrentConnectedDevice;
private LocalBluetoothManager mBluetoothManager;
@@ -83,6 +84,9 @@
if (!TextUtils.isEmpty(packageName)) {
mPackageName = packageName;
}
+
+ mVolumeAdjustmentForRemoteGroupSessions = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
}
@Override
@@ -387,7 +391,9 @@
@TargetApi(Build.VERSION_CODES.R)
boolean shouldEnableVolumeSeekBar(RoutingSessionInfo sessionInfo) {
- return false;
+ return sessionInfo.isSystemSession() // System sessions are not remote
+ || mVolumeAdjustmentForRemoteGroupSessions
+ || sessionInfo.getSelectedRoutes().size() <= 1;
}
private void refreshDevices() {
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 1844288..0b3eccf 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -85,9 +85,10 @@
val camSeed = Cam.fromInt(seedArgb)
val hue = camSeed.hue
val chroma = camSeed.chroma.coerceAtLeast(ACCENT1_CHROMA)
+ val tertiaryHue = wrapDegrees((hue + ACCENT3_HUE_SHIFT).toInt())
accent1 = Shades.of(hue, chroma).toList()
accent2 = Shades.of(hue, ACCENT2_CHROMA).toList()
- accent3 = Shades.of(hue + ACCENT3_HUE_SHIFT, ACCENT3_CHROMA).toList()
+ accent3 = Shades.of(tertiaryHue.toFloat(), ACCENT3_CHROMA).toList()
neutral1 = Shades.of(hue, NEUTRAL1_CHROMA).toList()
neutral2 = Shades.of(hue, NEUTRAL2_CHROMA).toList()
}
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4d2986f..7216ac6 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -266,8 +266,6 @@
<item name="android:paddingTop">12dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">24sp</item>
- <item name="android:singleLine">true</item>
- <item name="android:ellipsize">marquee</item>
</style>
<style name="TextAppearance.AuthCredential.Subtitle">
@@ -275,8 +273,6 @@
<item name="android:paddingTop">8dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">16sp</item>
- <item name="android:singleLine">true</item>
- <item name="android:ellipsize">marquee</item>
</style>
<style name="TextAppearance.AuthCredential.Description">
@@ -284,8 +280,6 @@
<item name="android:paddingTop">8dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">14sp</item>
- <item name="android:singleLine">true</item>
- <item name="android:ellipsize">marquee</item>
</style>
<style name="TextAppearance.AuthCredential.Error">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
index ee6dea5..91a3912 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
@@ -20,6 +20,11 @@
*/
interface FlagReader {
/** Returns a boolean value for the given flag. */
+ fun isEnabled(flag: BooleanFlag): Boolean {
+ return flag.default
+ }
+
+ /** Returns a boolean value for the given flag. */
fun isEnabled(id: Int, def: Boolean): Boolean {
return def
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/GroupTask.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/GroupTask.java
deleted file mode 100644
index 323b20e..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/GroupTask.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.recents.model;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * A group task in the recent tasks list.
- * TODO: Move this into Launcher
- */
-public class GroupTask {
- public @NonNull Task task1;
- public @Nullable Task task2;
-
- public GroupTask(@NonNull Task t1, @Nullable Task t2) {
- task1 = t1;
- task2 = t2;
- }
-
- public GroupTask(@NonNull GroupTask group) {
- task1 = new Task(group.task1);
- task2 = group.task2 != null
- ? new Task(group.task2)
- : null;
- }
-
- public boolean containsTask(int taskId) {
- return task1.key.id == taskId || (task2 != null && task2.key.id == taskId);
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 3f2ff74..3128ffd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -23,17 +23,14 @@
import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.ViewDebug;
-import com.android.systemui.shared.recents.utilities.Utilities;
+import androidx.annotation.Nullable;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Objects;
/**
@@ -202,8 +199,8 @@
* The icon is the task description icon (if provided), which falls back to the activity icon,
* which can then fall back to the application icon.
*/
- public Drawable icon;
- public ThumbnailData thumbnail;
+ @Nullable public Drawable icon;
+ @Nullable public ThumbnailData thumbnail;
@ViewDebug.ExportedProperty(category="recents")
@Deprecated
public String title;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index b95123d..38eded8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -195,8 +195,13 @@
}
@Override
- public void onTaskAppeared(RemoteAnimationTarget app) {
- animationHandler.onTaskAppeared(new RemoteAnimationTargetCompat(app));
+ public void onTasksAppeared(RemoteAnimationTarget[] apps) {
+ final RemoteAnimationTargetCompat[] compats =
+ new RemoteAnimationTargetCompat[apps.length];
+ for (int i = 0; i < apps.length; ++i) {
+ compats[i] = new RemoteAnimationTargetCompat(apps[i]);
+ }
+ animationHandler.onTasksAppeared(compats);
}
};
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index a74de2e..48f1b76 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -39,5 +39,5 @@
* Called when the task of an activity that has been started while the recents animation
* was running becomes ready for control.
*/
- void onTaskAppeared(RemoteAnimationTargetCompat app);
+ void onTasksAppeared(RemoteAnimationTargetCompat[] app);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 99b6aed..954cf9f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -53,6 +53,7 @@
import com.android.internal.util.DataClass;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import java.util.ArrayList;
import java.util.concurrent.Executor;
/**
@@ -127,7 +128,7 @@
mToken = transition;
// This transition is for opening recents, so recents is on-top. We want to draw
// the current going-away task on top of recents, though, so move it to front
- WindowContainerToken pausingTask = null;
+ final ArrayList<WindowContainerToken> pausingTasks = new ArrayList<>();
WindowContainerToken pipTask = null;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
@@ -138,7 +139,8 @@
if (taskInfo == null) {
continue;
}
- pausingTask = taskInfo.token;
+ // Add to front since we are iterating backwards.
+ pausingTasks.add(0, taskInfo.token);
if (taskInfo.pictureInPictureParams != null
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
pipTask = taskInfo.token;
@@ -150,7 +152,7 @@
t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);
}
t.apply();
- mRecentsSession.setup(controller, info, finishedCallback, pausingTask, pipTask,
+ mRecentsSession.setup(controller, info, finishedCallback, pausingTasks, pipTask,
leashMap, mToken);
recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0),
new Rect());
@@ -198,18 +200,18 @@
static class RecentsControllerWrap extends RecentsAnimationControllerCompat {
private RecentsAnimationControllerCompat mWrapped = null;
private IRemoteTransitionFinishedCallback mFinishCB = null;
- private WindowContainerToken mPausingTask = null;
+ private ArrayList<WindowContainerToken> mPausingTasks = null;
private WindowContainerToken mPipTask = null;
private TransitionInfo mInfo = null;
- private SurfaceControl mOpeningLeash = null;
+ private ArrayList<SurfaceControl> mOpeningLeashes = null;
private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
private PictureInPictureSurfaceTransaction mPipTransaction = null;
private IBinder mTransition = null;
void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
- IRemoteTransitionFinishedCallback finishCB, WindowContainerToken pausingTask,
- WindowContainerToken pipTask, ArrayMap<SurfaceControl, SurfaceControl> leashMap,
- IBinder transition) {
+ IRemoteTransitionFinishedCallback finishCB,
+ ArrayList<WindowContainerToken> pausingTasks, WindowContainerToken pipTask,
+ ArrayMap<SurfaceControl, SurfaceControl> leashMap, IBinder transition) {
if (mInfo != null) {
throw new IllegalStateException("Trying to run a new recents animation while"
+ " recents is already active.");
@@ -217,7 +219,7 @@
mWrapped = wrapped;
mInfo = info;
mFinishCB = finishCB;
- mPausingTask = pausingTask;
+ mPausingTasks = pausingTasks;
mPipTask = pipTask;
mLeashMap = leashMap;
mTransition = transition;
@@ -226,36 +228,57 @@
@SuppressLint("NewApi")
boolean merge(TransitionInfo info, SurfaceControl.Transaction t,
RecentsAnimationListener recents) {
- TransitionInfo.Change openingTask = null;
+ ArrayList<TransitionInfo.Change> openingTasks = null;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
if (change.getTaskInfo() != null) {
- if (openingTask != null) {
- Log.w(TAG, " Expecting to merge a task-open, but got >1 opening "
- + "tasks");
+ if (openingTasks == null) {
+ openingTasks = new ArrayList<>();
}
- openingTask = change;
+ openingTasks.add(change);
}
}
}
- if (openingTask == null) return false;
- mOpeningLeash = openingTask.getLeash();
- if (openingTask.getContainer().equals(mPausingTask)) {
- // In this case, we are "returning" to the already running app, so just consume
+ if (openingTasks == null) return false;
+ int pauseMatches = 0;
+ for (int i = 0; i < openingTasks.size(); ++i) {
+ if (mPausingTasks.contains(openingTasks.get(i).getContainer())) {
+ ++pauseMatches;
+ }
+ if (openingTasks.get(i).getContainer().equals(mPausingTasks.get(i))) {
+ // In this case, we are "returning" to an already running app, so just consume
+ // the merge and do nothing.
+ }
+ }
+ if (pauseMatches > 0) {
+ if (pauseMatches != mPausingTasks.size()) {
+ // We are not really "returning" properly... something went wrong.
+ throw new IllegalStateException("\"Concelling\" a recents transitions by "
+ + "unpausing " + pauseMatches + " apps after pausing "
+ + mPausingTasks.size() + " apps.");
+ }
+ // In this case, we are "returning" to an already running app, so just consume
// the merge and do nothing.
return true;
}
- // We are receiving a new opening task, so convert to onTaskAppeared.
final int layer = mInfo.getChanges().size() * 3;
- final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat(
- openingTask, layer, mInfo, t);
- mLeashMap.put(mOpeningLeash, target.leash.mSurfaceControl);
- t.reparent(target.leash.mSurfaceControl, mInfo.getRootLeash());
- t.setLayer(target.leash.mSurfaceControl, layer);
- t.hide(target.leash.mSurfaceControl);
- t.apply();
- recents.onTaskAppeared(target);
+ mOpeningLeashes = new ArrayList<>();
+ final RemoteAnimationTargetCompat[] targets =
+ new RemoteAnimationTargetCompat[openingTasks.size()];
+ for (int i = 0; i < openingTasks.size(); ++i) {
+ mOpeningLeashes.add(openingTasks.get(i).getLeash());
+ // We are receiving new opening tasks, so convert to onTasksAppeared.
+ final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat(
+ openingTasks.get(i), layer, mInfo, t);
+ mLeashMap.put(mOpeningLeashes.get(i), target.leash.mSurfaceControl);
+ t.reparent(target.leash.mSurfaceControl, mInfo.getRootLeash());
+ t.setLayer(target.leash.mSurfaceControl, layer);
+ t.hide(target.leash.mSurfaceControl);
+ t.apply();
+ targets[i] = target;
+ }
+ recents.onTasksAppeared(targets);
return true;
}
@@ -292,21 +315,26 @@
}
if (mWrapped != null) mWrapped.finish(toHome, sendUserLeaveHint);
try {
- if (!toHome && mPausingTask != null && mOpeningLeash == null) {
+ if (!toHome && mPausingTasks != null && mOpeningLeashes == null) {
// The gesture went back to opening the app rather than continuing with
// recents, so end the transition by moving the app back to the top (and also
// re-showing it's task).
final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.reorder(mPausingTask, true /* onTop */);
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.show(mInfo.getChange(mPausingTask).getLeash());
+ for (int i = mPausingTasks.size() - 1; i >= 0; ++i) {
+ // reverse order so that index 0 ends up on top
+ wct.reorder(mPausingTasks.get(i), true /* onTop */);
+ t.show(mInfo.getChange(mPausingTasks.get(i)).getLeash());
+ }
mFinishCB.onTransitionFinished(wct, t);
} else {
- if (mOpeningLeash != null) {
+ if (mOpeningLeashes != null) {
// TODO: the launcher animation should handle this
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.show(mOpeningLeash);
- t.setAlpha(mOpeningLeash, 1.f);
+ for (int i = 0; i < mOpeningLeashes.size(); ++i) {
+ t.show(mOpeningLeashes.get(i));
+ t.setAlpha(mOpeningLeashes.get(i), 1.f);
+ }
t.apply();
}
if (mPipTask != null && mPipTransaction != null) {
@@ -339,9 +367,9 @@
// Reset all members.
mWrapped = null;
mFinishCB = null;
- mPausingTask = null;
+ mPausingTasks = null;
mInfo = null;
- mOpeningLeash = null;
+ mOpeningLeashes = null;
mLeashMap = null;
mTransition = null;
}
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
index ef04619..acfa3c8 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
@@ -26,13 +26,16 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
+import androidx.annotation.BoolRes;
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.dump.DumpManager;
import com.android.systemui.util.settings.SecureSettings;
@@ -62,14 +65,19 @@
private final FlagManager mFlagManager;
private final SecureSettings mSecureSettings;
+ private final Resources mResources;
private final Map<Integer, Boolean> mBooleanFlagCache = new HashMap<>();
@Inject
- public FeatureFlagManager(FlagManager flagManager,
- SecureSettings secureSettings, Context context,
+ public FeatureFlagManager(
+ FlagManager flagManager,
+ Context context,
+ SecureSettings secureSettings,
+ @Main Resources resources,
DumpManager dumpManager) {
mFlagManager = flagManager;
mSecureSettings = secureSettings;
+ mResources = resources;
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_SET_FLAG);
filter.addAction(ACTION_GET_FLAGS);
@@ -77,17 +85,32 @@
dumpManager.registerDumpable(TAG, this);
}
- /** Return a {@link BooleanFlag}'s value. */
@Override
- public boolean isEnabled(int id, boolean defaultValue) {
+ public boolean isEnabled(BooleanFlag flag) {
+ int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
- Boolean result = isEnabledInternal(id);
- mBooleanFlagCache.put(id, result == null ? defaultValue : result);
+ boolean def = flag.getDefault();
+ if (flag.hasResourceOverride()) {
+ try {
+ def = isEnabledInOverlay(flag.getResourceOverride());
+ } catch (Resources.NotFoundException e) {
+ // no-op
+ }
+ }
+
+ mBooleanFlagCache.put(id, isEnabled(id, def));
}
return mBooleanFlagCache.get(id);
}
+ /** Return a {@link BooleanFlag}'s value. */
+ @Override
+ public boolean isEnabled(int id, boolean defaultValue) {
+ Boolean result = isEnabledInternal(id);
+ return result == null ? defaultValue : result;
+ }
+
/** Returns the stored value or null if not set. */
private Boolean isEnabledInternal(int id) {
try {
@@ -98,6 +121,10 @@
return null;
}
+ private boolean isEnabledInOverlay(@BoolRes int resId) {
+ return mResources.getBoolean(resId);
+ }
+
/** Set whether a given {@link BooleanFlag} is enabled or not. */
@Override
public void setEnabled(int id, boolean value) {
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
index 6ff175f..0934b32 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
@@ -16,7 +16,6 @@
package com.android.systemui.flags;
-import android.content.Context;
import android.util.SparseBooleanArray;
import androidx.annotation.NonNull;
@@ -24,7 +23,6 @@
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.util.settings.SecureSettings;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -41,8 +39,7 @@
public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
SparseBooleanArray mAccessedFlags = new SparseBooleanArray();
@Inject
- public FeatureFlagManager(
- SecureSettings secureSettings, Context context, DumpManager dumpManager) {
+ public FeatureFlagManager(DumpManager dumpManager) {
dumpManager.registerDumpable("SysUIFlags", this);
}
@@ -53,6 +50,11 @@
public void removeListener(Listener run) {}
@Override
+ public boolean isEnabled(BooleanFlag flag) {
+ return isEnabled(flag.getId(), flag.getDefault());
+ }
+
+ @Override
public boolean isEnabled(int key, boolean defaultValue) {
mAccessedFlags.append(key, defaultValue);
return defaultValue;
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index a383cab..ac463eb 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -31,7 +31,6 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.ViewController;
@@ -49,7 +48,6 @@
private final StatusBarStateController mStatusBarStateController;
private final BroadcastDispatcher mBroadcastDispatcher;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final KeyguardBypassController mBypassController;
private final BatteryController mBatteryController;
private final int mDozingColor = Color.WHITE;
private int mLockScreenColor;
@@ -71,14 +69,12 @@
BroadcastDispatcher broadcastDispatcher,
BatteryController batteryController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardBypassController bypassController,
@Main Resources resources
) {
super(view);
mStatusBarStateController = statusBarStateController;
mBroadcastDispatcher = broadcastDispatcher;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mBypassController = bypassController;
mBatteryController = batteryController;
mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index ef3104a..2a0c285 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -27,7 +27,6 @@
import android.widget.TextView;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import java.util.Calendar;
import java.util.Locale;
@@ -111,6 +110,28 @@
super.onDetachedFromWindow();
}
+ int getDozingWeight() {
+ if (useBoldedVersion()) {
+ return mDozingWeight + 100;
+ }
+ return mDozingWeight;
+ }
+
+ int getLockScreenWeight() {
+ if (useBoldedVersion()) {
+ return mLockScreenWeight + 100;
+ }
+ return mLockScreenWeight;
+ }
+
+ /**
+ * Whether to use a bolded version based on the user specified fontWeightAdjustment.
+ */
+ boolean useBoldedVersion() {
+ // "Bold text" fontWeightAdjustment is 300.
+ return getResources().getConfiguration().fontWeightAdjustment > 100;
+ }
+
void refreshTime() {
mTime.setTimeInMillis(System.currentTimeMillis());
setText(DateFormat.format(mFormat, mTime));
@@ -162,7 +183,7 @@
}
setTextStyle(
- mDozingWeight,
+ getDozingWeight(),
-1 /* text size, no update */,
mLockScreenColor,
false /* animate */,
@@ -171,7 +192,7 @@
null /* onAnimationEnd */);
setTextStyle(
- mLockScreenWeight,
+ getLockScreenWeight(),
-1 /* text size, no update */,
mLockScreenColor,
true, /* animate */
@@ -180,35 +201,22 @@
null /* onAnimationEnd */);
}
- void animateDisappear() {
- if (mTextAnimator == null) {
- return;
- }
-
- setTextStyle(
- 0 /* weight */,
- -1 /* text size, no update */,
- null /* color, no update */,
- true /* animate */,
- KeyguardBypassController.BYPASS_FADE_DURATION /* duration */,
- 0 /* delay */,
- null /* onAnimationEnd */);
- }
-
void animateCharge(DozeStateGetter dozeStateGetter) {
if (mTextAnimator == null || mTextAnimator.isRunning()) {
// Skip charge animation if dozing animation is already playing.
return;
}
Runnable startAnimPhase2 = () -> setTextStyle(
- dozeStateGetter.isDozing() ? mDozingWeight : mLockScreenWeight/* weight */,
+ dozeStateGetter.isDozing() ? getDozingWeight() : getLockScreenWeight() /* weight */,
-1,
null,
true /* animate */,
CHARGE_ANIM_DURATION_PHASE_1,
0 /* delay */,
null /* onAnimationEnd */);
- setTextStyle(dozeStateGetter.isDozing() ? mLockScreenWeight : mDozingWeight/* weight */,
+ setTextStyle(dozeStateGetter.isDozing()
+ ? getLockScreenWeight()
+ : getDozingWeight()/* weight */,
-1,
null,
true /* animate */,
@@ -218,7 +226,7 @@
}
void animateDoze(boolean isDozing, boolean animate) {
- setTextStyle(isDozing ? mDozingWeight : mLockScreenWeight /* weight */,
+ setTextStyle(isDozing ? getDozingWeight() : getLockScreenWeight() /* weight */,
-1,
isDozing ? mDozingColor : mLockScreenColor,
animate,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 1931c0a..905495d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -167,7 +167,6 @@
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
- mBypassController,
mResources);
mClockViewController.init();
@@ -178,7 +177,6 @@
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
- mBypassController,
mResources);
mLargeClockViewController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 85bc8f7..d27bc67 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2044,17 +2044,17 @@
}
/**
- * @return true if there's at least one udfps enrolled
+ * @return true if there's at least one udfps enrolled for the current user.
*/
public boolean isUdfpsEnrolled() {
return mIsUdfpsEnrolled;
}
/**
- * @return if udfps is available on this device. will return true even if the user hasn't
- * enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
+ * @return true if udfps HW is supported on this device. Can return true even if the user has
+ * not enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
*/
- public boolean isUdfpsAvailable() {
+ public boolean isUdfpsSupported() {
return mAuthController.getUdfpsProps() != null
&& !mAuthController.getUdfpsProps().isEmpty();
}
@@ -2102,7 +2102,7 @@
}
updateUdfpsEnrolled(getCurrentUser());
- final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsEnrolled());
+ final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsSupported());
final boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
|| mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
if (runningOrRestarting && !shouldListenForFingerprint) {
@@ -2407,7 +2407,7 @@
} else {
mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
mFingerprintAuthenticationCallback, null /* handler */,
- FingerprintManager.SENSOR_ID_ANY, userId);
+ FingerprintManager.SENSOR_ID_ANY, userId, 0 /* flags */);
}
setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
}
@@ -2990,7 +2990,7 @@
/**
* Register to receive notifications about general keyguard information
- * (see {@link InfoCallback}.
+ * (see {@link KeyguardUpdateMonitorCallback}.
*
* @param callback The callback to register
*/
@@ -3388,11 +3388,11 @@
+ " expected=" + (shouldListenForFingerprint(isUdfpsEnrolled()) ? 1 : 0));
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
- pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
pw.println(" mFingerprintLockedOut=" + mFingerprintLockedOut);
pw.println(" mFingerprintLockedOutPermanent=" + mFingerprintLockedOutPermanent);
pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId));
- if (isUdfpsEnrolled()) {
+ if (isUdfpsSupported()) {
+ pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
pw.println(" shouldListenForUdfps=" + shouldListenForFingerprint(true));
pw.println(" bouncerVisible=" + mBouncer);
pw.println(" mStatusBarState="
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index e115c34..b77db8f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -121,6 +121,13 @@
.setStartDelay(delay);
}
animator.start();
+ } else if (mUnlockedScreenOffAnimationController.shouldAnimateInKeyguard()) {
+ mKeyguardViewVisibilityAnimating = true;
+
+ // Ask the screen off animation controller to animate the keyguard visibility for us
+ // since it may need to be cancelled due to keyguard lifecycle events.
+ mUnlockedScreenOffAnimationController.animateInKeyguard(
+ mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
} else if (mLastOccludedState && !isOccluded) {
// An activity was displayed over the lock screen, and has now gone away
mView.setVisibility(View.VISIBLE);
@@ -132,13 +139,6 @@
.alpha(1f)
.withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
.start();
- } else if (mUnlockedScreenOffAnimationController.shouldAnimateInKeyguard()) {
- mKeyguardViewVisibilityAnimating = true;
-
- // Ask the screen off animation controller to animate the keyguard visibility for us
- // since it may need to be cancelled due to keyguard lifecycle events.
- mUnlockedScreenOffAnimationController.animateInKeyguard(
- mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
} else {
mView.setVisibility(View.VISIBLE);
mView.setAlpha(1f);
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 8a0b5b8..c7be3ce 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -435,7 +435,7 @@
boolean wasUdfpsSupported = mUdfpsSupported;
boolean wasUdfpsEnrolled = mUdfpsEnrolled;
- mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
+ mUdfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported();
mView.setUseBackground(mUdfpsSupported);
mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
index 59d9aff..d2703f5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -22,6 +22,7 @@
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
import static java.util.Objects.requireNonNull;
@@ -659,6 +660,7 @@
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT);
params.receiveInsetsIgnoringZOrder = true;
+ params.privateFlags |= PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
params.windowAnimations = android.R.style.Animation_Translucent;
params.gravity = Gravity.START | Gravity.TOP;
params.x = (mAlignment == Alignment.RIGHT) ? getMaxWindowX() : getMinWindowX();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index ec17d4e..90a1e5e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.PointF
@@ -29,7 +31,9 @@
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LiftReveal
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.Command
@@ -41,13 +45,10 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.ViewController
+import com.android.systemui.util.leak.RotationUtils
import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Provider
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.util.leak.RotationUtils
-
-private const val WAKE_AND_UNLOCK_FADE_DURATION = 180L
/***
* Controls the ripple effect that shows when authentication is successful.
@@ -141,11 +142,12 @@
private fun showUnlockedRipple() {
notificationShadeWindowController.setForcePluginOpen(true, this)
- val useCircleReveal = circleReveal != null && biometricUnlockController.isWakeAndUnlock
val lightRevealScrim = statusBar.lightRevealScrim
- if (useCircleReveal) {
- lightRevealScrim?.revealEffect = circleReveal!!
- startLightRevealScrimOnKeyguardFadingAway = true
+ if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
+ circleReveal?.let {
+ lightRevealScrim?.revealEffect = it
+ startLightRevealScrimOnKeyguardFadingAway = true
+ }
}
mView.startUnlockedRipple(
@@ -160,19 +162,29 @@
if (keyguardStateController.isKeyguardFadingAway) {
val lightRevealScrim = statusBar.lightRevealScrim
if (startLightRevealScrimOnKeyguardFadingAway && lightRevealScrim != null) {
- val revealAnimator = ValueAnimator.ofFloat(.1f, 1f).apply {
+ ValueAnimator.ofFloat(.1f, 1f).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
duration = RIPPLE_ANIMATION_DURATION
startDelay = keyguardStateController.keyguardFadingAwayDelay
addUpdateListener { animator ->
if (lightRevealScrim.revealEffect != circleReveal) {
- // if the something else took over the reveal, let's do nothing.
+ // if something else took over the reveal, let's do nothing.
return@addUpdateListener
}
lightRevealScrim.revealAmount = animator.animatedValue as Float
}
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ // Reset light reveal scrim to the default, so the StatusBar
+ // can handle any subsequent light reveal changes
+ // (ie: from dozing changes)
+ if (lightRevealScrim.revealEffect == circleReveal) {
+ lightRevealScrim.revealEffect = LiftReveal
+ }
+ }
+ })
+ start()
}
- revealAnimator.start()
startLightRevealScrimOnKeyguardFadingAway = false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index d64f9b3..34f4415 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -17,22 +17,11 @@
package com.android.systemui.flags;
import android.content.Context;
-import android.content.res.Resources;
import android.util.FeatureFlagUtils;
import android.util.Log;
-import android.util.SparseArray;
import android.widget.Toast;
-import androidx.annotation.BoolRes;
-
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
import javax.inject.Inject;
@@ -43,31 +32,13 @@
*/
@SysUISingleton
public class FeatureFlags {
- private final Resources mResources;
private final FlagReader mFlagReader;
private final Context mContext;
- private final Map<Integer, Flag<?>> mFlagMap = new HashMap<>();
- private final Map<Integer, List<Listener>> mListeners = new HashMap<>();
- private final SparseArray<Boolean> mCachedFlags = new SparseArray<>();
@Inject
- public FeatureFlags(@Main Resources resources, FlagReader flagReader, Context context) {
- mResources = resources;
+ public FeatureFlags(FlagReader flagReader, Context context) {
mFlagReader = flagReader;
mContext = context;
-
- flagReader.addListener(mListener);
- }
-
- private final FlagReader.Listener mListener = id -> {
- if (mListeners.containsKey(id) && mFlagMap.containsKey(id)) {
- mListeners.get(id).forEach(listener -> listener.onFlagChanged(mFlagMap.get(id)));
- }
- };
-
- @VisibleForTesting
- void addFlag(Flag<?> flag) {
- mFlagMap.put(flag.getId(), flag);
}
/**
@@ -75,32 +46,7 @@
* @return The value of the flag.
*/
public boolean isEnabled(BooleanFlag flag) {
- boolean def = flag.getDefault();
- if (flag.hasResourceOverride()) {
- try {
- def = isEnabledInOverlay(flag.getResourceOverride());
- } catch (Resources.NotFoundException e) {
- // no-op
- }
- }
- return mFlagReader.isEnabled(flag.getId(), def);
- }
-
- /**
- * @param flag The {@link IntFlag} of interest.
-
- /** Add a listener for a specific flag. */
- public void addFlagListener(Flag<?> flag, Listener listener) {
- mListeners.putIfAbsent(flag.getId(), new ArrayList<>());
- mListeners.get(flag.getId()).add(listener);
- mFlagMap.putIfAbsent(flag.getId(), flag);
- }
-
- /** Remove a listener for a specific flag. */
- public void removeFlagListener(Flag<?> flag, Listener listener) {
- if (mListeners.containsKey(flag.getId())) {
- mListeners.get(flag.getId()).remove(listener);
- }
+ return mFlagReader.isEnabled(flag);
}
public void assertLegacyPipelineEnabled() {
@@ -205,20 +151,4 @@
public static boolean isProviderModelSettingEnabled(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
-
- private boolean isEnabledInOverlay(@BoolRes int resId) {
- synchronized (mCachedFlags) {
- if (!mCachedFlags.contains(resId)) {
- mCachedFlags.put(resId, mResources.getBoolean(resId));
- }
-
- return mCachedFlags.get(resId);
- }
- }
-
- /** Simple interface for beinga alerted when a specific flag changes value. */
- public interface Listener {
- /** */
- void onFlagChanged(Flag<?> flag);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 42dd886..1981269 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -79,6 +79,7 @@
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
private final boolean mAboveStatusbar;
+ private final boolean mVolumeAdjustmentForRemoteGroupSessions;
private final NotificationEntryManager mNotificationEntryManager;
@VisibleForTesting
final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
@@ -111,6 +112,8 @@
mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
mUiEventLogger = uiEventLogger;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
}
void start(@NonNull Callback cb) {
@@ -477,7 +480,9 @@
}
boolean isVolumeControlEnabled(@NonNull MediaDevice device) {
- return !isActiveRemoteDevice(device);
+ // TODO(b/202500642): Also enable volume control for remote non-group sessions.
+ return !isActiveRemoteDevice(device)
+ || mVolumeAdjustmentForRemoteGroupSessions;
}
private final MediaController.Callback mCb = new MediaController.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 6a1eae7..bc023cc 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -24,9 +24,6 @@
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.containsType;
@@ -131,10 +128,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
-import com.android.systemui.shared.rotation.RotationButton;
-import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.rotation.RotationButton;
+import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.AutoHideUiElement;
@@ -614,8 +611,6 @@
mDeviceProvisionedController.addCallback(mUserSetupListener);
mNotificationShadeDepthController.addListener(mDepthListener);
- updateAccessibilityButtonModeIfNeeded();
-
return barView;
}
@@ -1406,34 +1401,6 @@
updateSystemUiStateFlags(a11yFlags);
}
- private void updateAccessibilityButtonModeIfNeeded() {
- final int mode = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
-
- // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
- // mode, so we don't need to update it.
- if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return;
- }
-
- // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
- // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
- if (QuickStepContract.isGesturalMode(mNavBarMode)
- && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
- Settings.Secure.putIntForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
- UserHandle.USER_CURRENT);
- // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
- // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
- } else if (!QuickStepContract.isGesturalMode(mNavBarMode)
- && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
- Settings.Secure.putIntForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
- }
- }
-
public void updateSystemUiStateFlags(int a11yFlags) {
if (a11yFlags < 0) {
a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
@@ -1551,6 +1518,9 @@
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
+ // update assistant entry points on system navigation radio button click
+ updateAssistantEntrypoints();
+
if (!QuickStepContract.isGesturalMode(mode)) {
// Reset the override alpha
if (getBarTransitions() != null) {
@@ -1558,7 +1528,6 @@
}
}
updateScreenPinningGestures();
- updateAccessibilityButtonModeIfNeeded();
if (!canShowSecondaryHandle()) {
resetSecondaryHandle();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 4959c7d..3dc79c4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -16,10 +16,14 @@
package com.android.systemui.navigationbar;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -27,6 +31,8 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
@@ -46,6 +52,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -142,6 +149,8 @@
}
final int oldMode = mNavMode;
mNavMode = mode;
+ updateAccessibilityButtonModeIfNeeded();
+
mHandler.post(() -> {
// create/destroy nav bar based on nav mode only in unfolded state
if (oldMode != mNavMode) {
@@ -157,6 +166,35 @@
});
}
+ private void updateAccessibilityButtonModeIfNeeded() {
+ ContentResolver contentResolver = mContext.getContentResolver();
+ final int mode = Settings.Secure.getIntForUser(contentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+
+ // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
+ // mode, so we don't need to update it.
+ if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return;
+ }
+
+ // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
+ // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
+ if (QuickStepContract.isGesturalMode(mNavMode)
+ && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
+ Settings.Secure.putIntForUser(contentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
+ UserHandle.USER_CURRENT);
+ // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
+ // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
+ } else if (!QuickStepContract.isGesturalMode(mNavMode)
+ && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
+ Settings.Secure.putIntForUser(contentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+ }
+ }
+
/** @see #initializeTaskbarIfNecessary() */
private boolean updateNavbarForTaskbar() {
boolean taskbarShown = initializeTaskbarIfNecessary();
@@ -222,6 +260,8 @@
*/
public void createNavigationBars(final boolean includeDefaultDisplay,
RegisterStatusBarResult result) {
+ updateAccessibilityButtonModeIfNeeded();
+
// Don't need to create nav bar on the default display if we initialize TaskBar.
final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
&& !initializeTaskbarIfNecessary();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 74ebfe5..1c00887 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -818,7 +818,7 @@
}
private void showTryFingerprintMsg(int msgId, String a11yString) {
- if (mKeyguardUpdateMonitor.isUdfpsAvailable()) {
+ if (mKeyguardUpdateMonitor.isUdfpsSupported()) {
// if udfps available, there will always be a tappable affordance to unlock
// For example, the lock icon
if (mKeyguardBypassController.getUserHasDeviceEntryIntent()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index cbb3aba..da2b85e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -173,7 +173,7 @@
}
// Record the to-be mState and mLastState
- recordHistoricalState(state, mState);
+ recordHistoricalState(state /* newState */, mState /* lastState */, false);
// b/139259891
if (mState == StatusBarState.SHADE && state == StatusBarState.SHADE_LOCKED) {
@@ -206,6 +206,7 @@
@Override
public void setUpcomingState(int nextState) {
mUpcomingState = nextState;
+ recordHistoricalState(mUpcomingState /* newState */, mState /* lastState */, true);
}
@Override
@@ -505,31 +506,36 @@
}
}
- private void recordHistoricalState(int currentState, int lastState) {
+ private void recordHistoricalState(int newState, int lastState, boolean upcoming) {
mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
HistoricalState state = mHistoricalRecords[mHistoryIndex];
- state.mState = currentState;
+ state.mNewState = newState;
state.mLastState = lastState;
state.mTimestamp = System.currentTimeMillis();
+ state.mUpcoming = upcoming;
}
/**
* For keeping track of our previous state to help with debugging
*/
private static class HistoricalState {
- int mState;
+ int mNewState;
int mLastState;
long mTimestamp;
+ boolean mUpcoming;
@Override
public String toString() {
if (mTimestamp != 0) {
StringBuilder sb = new StringBuilder();
- sb.append("state=").append(mState)
- .append(" (").append(describe(mState)).append(")");
- sb.append("lastState=").append(mLastState).append(" (").append(describe(mLastState))
+ if (mUpcoming) {
+ sb.append("upcoming-");
+ }
+ sb.append("newState=").append(mNewState)
+ .append("(").append(describe(mNewState)).append(")");
+ sb.append(" lastState=").append(mLastState).append("(").append(describe(mLastState))
.append(")");
- sb.append("timestamp=")
+ sb.append(" timestamp=")
.append(DateFormat.format("MM-dd HH:mm:ss", mTimestamp));
return sb.toString();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index e273727..32659e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2206,7 +2206,7 @@
mQs.setExpanded(mQsExpanded);
}
- private void setQsExpansion(float height) {
+ void setQsExpansion(float height) {
height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling
@@ -2250,7 +2250,13 @@
int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
setQSClippingBounds();
- mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+
+ // Only need to notify the notification stack when we're not in split screen mode. If we
+ // do, then the notification panel starts scrolling along with the QS.
+ if (!mShouldUseSplitNotificationShade) {
+ mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+ }
+
mDepthController.setQsPanelExpansion(qsExpansionFraction);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 91df1c5..cbaa468 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -230,7 +230,6 @@
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
-import com.android.systemui.statusbar.window.StatusBarWindowView;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.DumpUtilsKt;
import com.android.systemui.util.WallpaperController;
@@ -2386,6 +2385,8 @@
if (mLightRevealScrim != null) {
pw.println(
+ "mLightRevealScrim.getRevealEffect(): " + mLightRevealScrim.getRevealEffect());
+ pw.println(
"mLightRevealScrim.getRevealAmount(): " + mLightRevealScrim.getRevealAmount());
}
@@ -3371,17 +3372,24 @@
return;
}
- if (wakingUp && mWakefulnessLifecycle.getLastWakeReason()
- == PowerManager.WAKE_REASON_POWER_BUTTON
- || !wakingUp && mWakefulnessLifecycle.getLastSleepReason()
- == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON) {
+ final boolean wakingUpFromPowerButton = wakingUp
+ && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)
+ && mWakefulnessLifecycle.getLastWakeReason()
+ == PowerManager.WAKE_REASON_POWER_BUTTON;
+ final boolean sleepingFromPowerButton = !wakingUp
+ && mWakefulnessLifecycle.getLastSleepReason()
+ == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON;
+
+ if (wakingUpFromPowerButton || sleepingFromPowerButton) {
mLightRevealScrim.setRevealEffect(mPowerButtonReveal);
+ mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount());
} else if (!wakingUp || !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
// If we're going to sleep, but it's not from the power button, use the default reveal.
// If we're waking up, only use the default reveal if the biometric controller didn't
// already set it to the circular reveal because we're waking up from a fingerprint/face
// auth.
mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
+ mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index cd5865f..426bc91 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -259,8 +259,13 @@
if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
reevaluateSystemTheme(true /* forceReload */);
} else if (Intent.ACTION_WALLPAPER_CHANGED.equals(intent.getAction())) {
- mAcceptColorEvents = true;
- Log.i(TAG, "Allowing color events again");
+ if (intent.getBooleanExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, false)) {
+ mAcceptColorEvents = true;
+ Log.i(TAG, "Wallpaper changed, allowing color events again");
+ } else {
+ Log.i(TAG, "Wallpaper changed from background app, "
+ + "keep deferring color events. Accepting: " + mAcceptColorEvents);
+ }
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index e570598..cd6a778 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -33,7 +33,11 @@
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.IVolumeController;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
+import android.media.RoutingSessionInfo;
import android.media.VolumePolicy;
+import android.media.session.MediaController;
import android.media.session.MediaController.PlaybackInfo;
import android.media.session.MediaSession.Token;
import android.net.Uri;
@@ -71,6 +75,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -118,6 +123,7 @@
private final Context mContext;
private final Looper mWorkerLooper;
private final PackageManager mPackageManager;
+ private final MediaRouter2Manager mRouter2Manager;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private AudioManager mAudio;
private IAudioService mAudioService;
@@ -179,6 +185,7 @@
mWorkerLooper = theadFactory.buildLooperOnNewThread(
VolumeDialogControllerImpl.class.getSimpleName());
mWorker = new W(mWorkerLooper);
+ mRouter2Manager = MediaRouter2Manager.getInstance(mContext);
mMediaSessionsCallbacksW = new MediaSessionsCallbacks(mContext);
mMediaSessions = createMediaSessions(mContext, mWorkerLooper, mMediaSessionsCallbacksW);
mAudio = audioManager;
@@ -1149,16 +1156,16 @@
private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>();
private int mNextStream = DYNAMIC_STREAM_START_INDEX;
- private final boolean mShowRemoteSessions;
+ private final boolean mVolumeAdjustmentForRemoteGroupSessions;
public MediaSessionsCallbacks(Context context) {
- mShowRemoteSessions = context.getResources().getBoolean(
- com.android.internal.R.bool.config_volumeShowRemoteSessions);
+ mVolumeAdjustmentForRemoteGroupSessions = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
}
@Override
public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) {
- if (mShowRemoteSessions) {
+ if (showForSession(token)) {
addStream(token, "onRemoteUpdate");
int stream = 0;
@@ -1190,7 +1197,7 @@
@Override
public void onRemoteVolumeChanged(Token token, int flags) {
- if (mShowRemoteSessions) {
+ if (showForSession(token)) {
addStream(token, "onRemoteVolumeChanged");
int stream = 0;
synchronized (mRemoteStreams) {
@@ -1214,7 +1221,7 @@
@Override
public void onRemoteRemoved(Token token) {
- if (mShowRemoteSessions) {
+ if (showForSession(token)) {
int stream = 0;
synchronized (mRemoteStreams) {
if (!mRemoteStreams.containsKey(token)) {
@@ -1233,14 +1240,41 @@
}
public void setStreamVolume(int stream, int level) {
- if (mShowRemoteSessions) {
- final Token t = findToken(stream);
- if (t == null) {
- Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
- return;
- }
- mMediaSessions.setVolume(t, level);
+ final Token token = findToken(stream);
+ if (token == null) {
+ Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
+ return;
}
+ if (showForSession(token)) {
+ mMediaSessions.setVolume(token, level);
+ }
+ }
+
+ private boolean showForSession(Token token) {
+ if (mVolumeAdjustmentForRemoteGroupSessions) {
+ return true;
+ }
+ MediaController ctr = new MediaController(mContext, token);
+ String packageName = ctr.getPackageName();
+ List<RoutingSessionInfo> sessions =
+ mRouter2Manager.getRoutingSessions(packageName);
+ boolean foundNonSystemSession = false;
+ boolean isGroup = false;
+ for (RoutingSessionInfo session : sessions) {
+ if (!session.isSystemSession()) {
+ foundNonSystemSession = true;
+ int selectedRouteCount = session.getSelectedRoutes().size();
+ if (selectedRouteCount > 1) {
+ isGroup = true;
+ break;
+ }
+ }
+ }
+ if (!foundNonSystemSession) {
+ Log.d(TAG, "No routing session for " + packageName);
+ return false;
+ }
+ return !isGroup;
}
private Token findToken(int stream) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 1ee6f70..ff5960b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -472,7 +472,8 @@
mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
mTestableLooper.processAllMessages();
- verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt());
+ verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+ anyInt());
verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
index 2fa32ba..6347638 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
@@ -53,8 +53,6 @@
public class FeatureFlagManagerTest extends SysuiTestCase {
FeatureFlagManager mFeatureFlagManager;
- @Mock private FlagManager mFlagManager;
- @Mock private SecureSettings mSecureSettings;
@Mock private Context mContext;
@Mock private DumpManager mDumpManager;
@@ -62,14 +60,11 @@
public void setup() {
MockitoAnnotations.initMocks(this);
- mFeatureFlagManager = new FeatureFlagManager(mSecureSettings, mContext, mDumpManager);
+ mFeatureFlagManager = new FeatureFlagManager(mDumpManager);
}
@After
public void onFinished() {
- // SecureSettings and Context are provided for constructor consistency with the
- // debug version of the FeatureFlagManager, but should never be used.
- verifyZeroInteractions(mSecureSettings, mContext);
// The dump manager should be registered with even for the release version, but that's it.
verify(mDumpManager).registerDumpable(anyString(), any());
verifyNoMoreInteractions(mDumpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
deleted file mode 100644
index 30e9b51..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.flags;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Resources;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
-@SmallTest
-public class FeatureFlagsTest extends SysuiTestCase {
-
- @Mock Resources mResources;
- @Mock FlagReader mFeatureFlagReader;
-
- private FeatureFlags mFeatureFlags;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- when(mFeatureFlagReader.isEnabled(anyInt(), anyBoolean())).thenAnswer(
- (Answer<Boolean>) invocation -> invocation.getArgument(1));
-
- mFeatureFlags = new FeatureFlags(mResources, mFeatureFlagReader, getContext());
- }
-
- @Test
- public void testAddListener() {
- Flag<?> flag = new BooleanFlag(1);
- mFeatureFlags.addFlag(flag);
-
- // Assert and capture that a plugin listener was added.
- ArgumentCaptor<FlagReader.Listener> pluginListenerCaptor =
- ArgumentCaptor.forClass(FlagReader.Listener.class);
- verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
- FlagReader.Listener pluginListener = pluginListenerCaptor.getValue();
-
- // Signal a change. No listeners, so no real effect.
- pluginListener.onFlagChanged(flag.getId());
-
- // Add a listener for the flag
- final Flag<?>[] changedFlag = {null};
- FeatureFlags.Listener listener = f -> changedFlag[0] = f;
- mFeatureFlags.addFlagListener(flag, listener);
-
- // No changes seen yet.
- assertThat(changedFlag[0]).isNull();
-
- // Signal a change.
- pluginListener.onFlagChanged(flag.getId());
-
- // Assert that the change was for the correct flag.
- assertThat(changedFlag[0]).isEqualTo(flag);
- }
-
- @Test
- public void testRemoveListener() {
- Flag<?> flag = new BooleanFlag(1);
- mFeatureFlags.addFlag(flag);
-
- // Assert and capture that a plugin listener was added.
- ArgumentCaptor<FlagReader.Listener> pluginListenerCaptor =
- ArgumentCaptor.forClass(FlagReader.Listener.class);
- verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
- FlagReader.Listener pluginListener = pluginListenerCaptor.getValue();
-
- // Add a listener for the flag
- final Flag<?>[] changedFlag = {null};
- FeatureFlags.Listener listener = f -> changedFlag[0] = f;
- mFeatureFlags.addFlagListener(flag, listener);
-
- // Signal a change.
- pluginListener.onFlagChanged(flag.getId());
-
- // Assert that the change was for the correct flag.
- assertThat(changedFlag[0]).isEqualTo(flag);
-
- changedFlag[0] = null;
-
- // Now remove the listener.
- mFeatureFlags.removeFlagListener(flag, listener);
- // Signal a change.
- pluginListener.onFlagChanged(flag.getId());
- // Assert that the change was not triggered
- assertThat(changedFlag[0]).isNull();
- }
-
- @Test
- public void testBooleanDefault() {
- BooleanFlag flag = new BooleanFlag(1, true);
-
- mFeatureFlags.addFlag(flag);
-
- assertThat(mFeatureFlags.isEnabled(flag)).isTrue();
- }
-
- @Test
- public void testBooleanResourceOverlay() {
- int resourceId = 12;
- BooleanFlag flag = new BooleanFlag(1, false, resourceId);
- when(mResources.getBoolean(resourceId)).thenReturn(true);
- when(mResources.getResourceEntryName(resourceId)).thenReturn("flag");
-
- mFeatureFlags.addFlag(flag);
-
- assertThat(mFeatureFlags.isEnabled(flag)).isTrue();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
index df11284..5a4bb86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
@@ -40,7 +40,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.BatteryController;
import org.junit.After;
@@ -69,8 +68,6 @@
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
- private KeyguardBypassController mBypassController;
- @Mock
private Resources mResources;
private MockitoSession mStaticMockSession;
@@ -99,7 +96,6 @@
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
- mBypassController,
mResources
);
mAnimatableClockController.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 5e73dbc..d64319b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -212,6 +212,7 @@
@Test
public void testUpdateFingerprintLocationOnAuthenticatorsRegistered() {
// GIVEN fp sensor location is not available pre-init
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
when(mAuthController.getUdfpsProps()).thenReturn(null);
mLockIconViewController.init();
@@ -232,7 +233,7 @@
}
@Test
- public void testLockIconViewBackgroundEnabledWhenUdfpsIsAvailable() {
+ public void testLockIconViewBackgroundEnabledWhenUdfpsIsSupported() {
// GIVEN Udpfs sensor location is available
setupUdfps();
@@ -247,9 +248,9 @@
}
@Test
- public void testLockIconViewBackgroundDisabledWhenUdfpsIsUnavailable() {
- // GIVEN Udfps sensor location is not available
- when(mAuthController.getUdfpsSensorLocation()).thenReturn(null);
+ public void testLockIconViewBackgroundDisabledWhenUdfpsIsNotSupported() {
+ // GIVEN Udfps sensor location is not supported
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
mLockIconViewController.init();
captureAttachListener();
@@ -365,6 +366,7 @@
}
private Pair<Integer, PointF> setupUdfps() {
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
final PointF udfpsLocation = new PointF(50, 75);
final int radius = 33;
final FingerprintSensorPropertiesInternal fpProps =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
index bc86ef9..8cd7d94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
@@ -22,6 +22,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.graphics.cam.Cam;
import com.android.systemui.SysuiTestCase;
import org.junit.Assert;
@@ -90,4 +91,13 @@
List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
Assert.assertEquals(rankedSeedColors, List.of(0xffaec00a, 0xffbe0000, 0xffcc040f));
}
+
+ @Test
+ public void testTertiaryHueWrapsProperly() {
+ int colorInt = 0xffB3588A; // H350 C50 T50
+ ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */);
+ int tertiaryMid = colorScheme.getAccent3().get(colorScheme.getAccent3().size() / 2);
+ Cam cam = Cam.fromInt(tertiaryMid);
+ Assert.assertEquals(cam.getHue(), 50.0, 10.0);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 01f7fae..cb0d87a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -701,7 +701,7 @@
// GIVEN fingerprint is also running (not udfps)
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
- when(mKeyguardUpdateMonitor.isUdfpsAvailable()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
mController.setVisible(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index f89bbe8..766471b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -152,7 +152,7 @@
}
@Test
- public void onWallpaperColorsChanged_setsTheme() {
+ public void onWallpaperColorsChanged_setsTheme_whenForeground() {
// Should ask for a new theme when wallpaper colors change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
@@ -180,13 +180,43 @@
// But should change theme after changing wallpapers
clearInvocations(mThemeOverlayApplier);
- mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
+ Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
+ intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, true);
+ mBroadcastReceiver.getValue().onReceive(null, intent);
mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
null, null), WallpaperManager.FLAG_SYSTEM);
verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
}
@Test
+ public void onWallpaperColorsChanged_setsTheme_skipWhenBackground() {
+ // Should ask for a new theme when wallpaper colors change
+ WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+ Color.valueOf(Color.BLUE), null);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
+ ArgumentCaptor.forClass(Map.class);
+
+ verify(mThemeOverlayApplier)
+ .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+
+ // Assert that we received the colors that we were expecting
+ assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
+ .isEqualTo(new OverlayIdentifier("ffff0000"));
+ assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
+ .isEqualTo(new OverlayIdentifier("ffff0000"));
+
+ // Should not change theme after changing wallpapers, if intent doesn't have
+ // WallpaperManager.EXTRA_FROM_FOREGROUND_APP set to true.
+ clearInvocations(mThemeOverlayApplier);
+ mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
+ mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
+ null, null), WallpaperManager.FLAG_SYSTEM);
+ verify(mThemeOverlayApplier, never())
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ }
+
+ @Test
public void onWallpaperColorsChanged_preservesWallpaperPickerTheme() {
// Should ask for a new theme when wallpaper colors change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
@@ -455,7 +485,9 @@
// Regression test: null events should not reset the internal state and allow colors to be
// applied again.
clearInvocations(mThemeOverlayApplier);
- mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
+ Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
+ intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, true);
+ mBroadcastReceiver.getValue().onReceive(null, intent);
mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM);
verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 5c0efd3..c9462d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -101,6 +101,11 @@
// Initial non-set value
when(mRingerModeLiveData.getValue()).thenReturn(-1);
when(mRingerModeInternalLiveData.getValue()).thenReturn(-1);
+ // Enable group volume adjustments
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions,
+ true);
+
mCallback = mock(VolumeDialogControllerImpl.C.class);
mThreadFactory.setLooper(TestableLooper.get(this).getLooper());
mVolumeController = new TestableVolumeDialogControllerImpl(mContext,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cd743f9..b5f3389 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -31,7 +31,7 @@
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
-import static android.app.ActivityManager.StopBgUsersOnSwitch;
+import static android.app.ActivityManager.StopUserOnSwitch;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.AppOpsManager.OP_NONE;
@@ -15099,8 +15099,8 @@
}
@Override
- public void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
- mUserController.setStopBackgroundUsersOnSwitch(value);
+ public void setStopUserOnSwitch(@StopUserOnSwitch int value) {
+ mUserController.setStopUserOnSwitch(value);
}
@Override
@@ -16402,8 +16402,8 @@
}
@Override
- public void setStopBackgroundUsersOnSwitch(int value) {
- ActivityManagerService.this.setStopBackgroundUsersOnSwitch(value);
+ public void setStopUserOnSwitch(int value) {
+ ActivityManagerService.this.setStopUserOnSwitch(value);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 60b2149..31e48fb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -331,7 +331,7 @@
case "get-isolated-pids":
return runGetIsolatedProcesses(pw);
case "set-stop-user-on-switch":
- return runSetStopBackgroundUsersOnSwitch(pw);
+ return runSetStopUserOnSwitch(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -3166,25 +3166,24 @@
return 0;
}
- private int runSetStopBackgroundUsersOnSwitch(PrintWriter pw) throws RemoteException {
+ private int runSetStopUserOnSwitch(PrintWriter pw) throws RemoteException {
mInternal.enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "setStopBackgroundUsersOnSwitch()");
+ "setStopUserOnSwitch()");
String arg = getNextArg();
if (arg == null) {
- Slogf.i(TAG, "runSetStopBackgroundUsersOnSwitch(): resetting to default value");
- mInternal.setStopBackgroundUsersOnSwitch(
- ActivityManager.STOP_BG_USERS_ON_SWITCH_DEFAULT);
+ Slogf.i(TAG, "setStopUserOnSwitch(): resetting to default value");
+ mInternal.setStopUserOnSwitch(ActivityManager.STOP_USER_ON_SWITCH_DEFAULT);
pw.println("Reset to default value");
return 0;
}
boolean stop = Boolean.parseBoolean(arg);
int value = stop
- ? ActivityManager.STOP_BG_USERS_ON_SWITCH_TRUE
- : ActivityManager.STOP_BG_USERS_ON_SWITCH_FALSE;
+ ? ActivityManager.STOP_USER_ON_SWITCH_TRUE
+ : ActivityManager.STOP_USER_ON_SWITCH_FALSE;
- Slogf.i(TAG, "runSetStopBackgroundUsersOnSwitch(): setting to %d (%b)", value, stop);
- mInternal.setStopBackgroundUsersOnSwitch(value);
+ Slogf.i(TAG, "runSetStopUserOnSwitch(): setting to %d (%b)", value, stop);
+ mInternal.setStopUserOnSwitch(value);
pw.println("Set to " + stop);
return 0;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index b8be6c5..319fa94 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -19,9 +19,9 @@
import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.app.ActivityManager.STOP_BG_USERS_ON_SWITCH_DEFAULT;
-import static android.app.ActivityManager.STOP_BG_USERS_ON_SWITCH_TRUE;
-import static android.app.ActivityManager.StopBgUsersOnSwitch;
+import static android.app.ActivityManager.STOP_USER_ON_SWITCH_DEFAULT;
+import static android.app.ActivityManager.STOP_USER_ON_SWITCH_TRUE;
+import static android.app.ActivityManager.StopUserOnSwitch;
import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
@@ -376,7 +376,7 @@
* user is switched.
*/
@GuardedBy("mLock")
- private @StopBgUsersOnSwitch int mStopBgUsersOnSwitch = STOP_BG_USERS_ON_SWITCH_DEFAULT;
+ private @StopUserOnSwitch int mStopUserOnSwitch = STOP_USER_ON_SWITCH_DEFAULT;
UserController(ActivityManagerService service) {
this(new Injector(service));
@@ -418,29 +418,27 @@
}
}
- void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+ void setStopUserOnSwitch(@StopUserOnSwitch int value) {
if (mInjector.checkCallingPermission(android.Manifest.permission.MANAGE_USERS)
== PackageManager.PERMISSION_DENIED && mInjector.checkCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
== PackageManager.PERMISSION_DENIED) {
throw new SecurityException(
"You either need MANAGE_USERS or INTERACT_ACROSS_USERS_FULL permission to "
- + "call setStopBackgroundUsersOnSwitch()");
+ + "call setStopUserOnSwitch()");
}
synchronized (mLock) {
- Slogf.i(TAG, "setStopBackgroundUsersOnSwitch(): %d -> %d",
- mStopBgUsersOnSwitch, value);
- mStopBgUsersOnSwitch = value;
+ Slogf.i(TAG, "setStopUserOnSwitch(): %d -> %d", mStopUserOnSwitch, value);
+ mStopUserOnSwitch = value;
}
}
- private boolean shouldStopBackgroundUsersOnSwitch() {
+ private boolean shouldStopUserOnSwitch() {
synchronized (mLock) {
- if (mStopBgUsersOnSwitch != STOP_BG_USERS_ON_SWITCH_DEFAULT) {
- final boolean value = mStopBgUsersOnSwitch == STOP_BG_USERS_ON_SWITCH_TRUE;
- Slogf.i(TAG, "isStopBackgroundUsersOnSwitch(): returning overridden value (%b)",
- value);
+ if (mStopUserOnSwitch != STOP_USER_ON_SWITCH_DEFAULT) {
+ final boolean value = mStopUserOnSwitch == STOP_USER_ON_SWITCH_TRUE;
+ Slogf.i(TAG, "shouldStopUserOnSwitch(): returning overridden value (%b)", value);
return value;
}
}
@@ -1834,7 +1832,7 @@
mUserSwitchObservers.finishBroadcast();
}
- private void stopBackgroundUsersOnSwitchIfEnforced(@UserIdInt int oldUserId) {
+ private void stopUserOnSwitchIfEnforced(@UserIdInt int oldUserId) {
// Never stop system user
if (oldUserId == UserHandle.USER_SYSTEM) {
return;
@@ -1842,18 +1840,17 @@
boolean hasRestriction =
hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, oldUserId);
synchronized (mLock) {
- // If running in background is disabled or mStopBackgroundUsersOnSwitch mode,
- // stop the user.
- boolean disallowRunInBg = hasRestriction || shouldStopBackgroundUsersOnSwitch();
+ // If running in background is disabled or mStopUserOnSwitch mode, stop the user.
+ boolean disallowRunInBg = hasRestriction || shouldStopUserOnSwitch();
if (!disallowRunInBg) {
if (DEBUG_MU) {
- Slogf.i(TAG, "stopBackgroundUsersIfEnforced() NOT stopping %d and related "
- + "users", oldUserId);
+ Slogf.i(TAG, "stopUserOnSwitchIfEnforced() NOT stopping %d and related users",
+ oldUserId);
}
return;
}
if (DEBUG_MU) {
- Slogf.i(TAG, "stopBackgroundUsersIfEnforced() stopping %d and related users",
+ Slogf.i(TAG, "stopUserOnSwitchIfEnforced() stopping %d and related users",
oldUserId);
}
stopUsersLU(oldUserId, /* force= */ false, /* allowDelayedLocking= */ true,
@@ -1956,7 +1953,7 @@
mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0));
stopGuestOrEphemeralUserIfBackground(oldUserId);
- stopBackgroundUsersOnSwitchIfEnforced(oldUserId);
+ stopUserOnSwitchIfEnforced(oldUserId);
}
private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) {
@@ -2646,9 +2643,8 @@
pw.println(" mTargetUserId:" + mTargetUserId);
pw.println(" mLastActiveUsers:" + mLastActiveUsers);
pw.println(" mDelayUserDataLocking:" + mDelayUserDataLocking);
- pw.println(" shouldStopBackgroundUsersOnSwitch():"
- + shouldStopBackgroundUsersOnSwitch());
- pw.println(" mStopBgUsersOnSwitch:" + mStopBgUsersOnSwitch);
+ pw.println(" shouldStopUserOnSwitch():" + shouldStopUserOnSwitch());
+ pw.println(" mStopUserOnSwitch:" + mStopUserOnSwitch);
pw.println(" mMaxRunningUsers:" + mMaxRunningUsers);
pw.println(" mUserSwitchUiEnabled:" + mUserSwitchUiEnabled);
pw.println(" mInitialized:" + mInitialized);
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index e0775d4..f42870b 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -1381,7 +1381,8 @@
Slog.d(TAG, "handleAuthenticate: modality(" + preAuthStatus.first
+ "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo
- + " requestId: " + requestId);
+ + " requestId: " + requestId + " promptInfo.isIgnoreEnrollmentState: "
+ + promptInfo.isIgnoreEnrollmentState());
if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) {
// If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index cd0ff10..a5a3542 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -83,6 +83,7 @@
final List<Pair<BiometricSensor, Integer>> ineligibleSensors;
final boolean credentialAvailable;
final boolean confirmationRequested;
+ final boolean ignoreEnrollmentState;
static PreAuthInfo create(ITrustManager trustManager,
DevicePolicyManager devicePolicyManager,
@@ -114,7 +115,8 @@
@AuthenticatorStatus int status = getStatusForBiometricAuthenticator(
devicePolicyManager, settingObserver, sensor, userId, opPackageName,
checkDevicePolicyManager, requestedStrength,
- promptInfo.getAllowedSensorIds());
+ promptInfo.getAllowedSensorIds(),
+ promptInfo.isIgnoreEnrollmentState());
Slog.d(TAG, "Package: " + opPackageName
+ " Sensor ID: " + sensor.id
@@ -130,7 +132,8 @@
}
return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
- eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested);
+ eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested,
+ promptInfo.isIgnoreEnrollmentState());
}
/**
@@ -145,7 +148,8 @@
BiometricService.SettingObserver settingObserver,
BiometricSensor sensor, int userId, String opPackageName,
boolean checkDevicePolicyManager, int requestedStrength,
- @NonNull List<Integer> requestedSensorIds) {
+ @NonNull List<Integer> requestedSensorIds,
+ boolean ignoreEnrollmentState) {
if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) {
return BIOMETRIC_NO_HARDWARE;
@@ -167,7 +171,8 @@
return BIOMETRIC_HARDWARE_NOT_DETECTED;
}
- if (!sensor.impl.hasEnrolledTemplates(userId, opPackageName)) {
+ if (!sensor.impl.hasEnrolledTemplates(userId, opPackageName)
+ && !ignoreEnrollmentState) {
return BIOMETRIC_NOT_ENROLLED;
}
@@ -238,7 +243,7 @@
private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
boolean credentialRequested, List<BiometricSensor> eligibleSensors,
List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
- boolean confirmationRequested) {
+ boolean confirmationRequested, boolean ignoreEnrollmentState) {
mBiometricRequested = biometricRequested;
mBiometricStrengthRequested = biometricStrengthRequested;
this.credentialRequested = credentialRequested;
@@ -247,6 +252,7 @@
this.ineligibleSensors = ineligibleSensors;
this.credentialAvailable = credentialAvailable;
this.confirmationRequested = confirmationRequested;
+ this.ignoreEnrollmentState = ignoreEnrollmentState;
}
private Pair<BiometricSensor, Integer> calculateErrorByPriority() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index f35bb7f..c5d33ed 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -281,7 +281,7 @@
@Override // Binder call
public long authenticate(final IBinder token, final long operationId,
final int sensorId, final int userId, final IFingerprintServiceReceiver receiver,
- final String opPackageName) {
+ final String opPackageName, boolean ignoreEnrollmentState) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -333,7 +333,8 @@
&& sensorProps != null && sensorProps.isAnyUdfpsType()) {
identity = Binder.clearCallingIdentity();
try {
- return authenticateWithPrompt(operationId, sensorProps, userId, receiver);
+ return authenticateWithPrompt(operationId, sensorProps, userId, receiver,
+ ignoreEnrollmentState);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -347,7 +348,8 @@
final long operationId,
@NonNull final FingerprintSensorPropertiesInternal props,
final int userId,
- final IFingerprintServiceReceiver receiver) {
+ final IFingerprintServiceReceiver receiver,
+ boolean ignoreEnrollmentState) {
final Context context = getUiContext();
final Executor executor = context.getMainExecutor();
@@ -368,6 +370,7 @@
})
.setAllowedSensorIds(new ArrayList<>(
Collections.singletonList(props.sensorId)))
+ .setIgnoreEnrollmentState(ignoreEnrollmentState)
.build();
final BiometricPrompt.AuthenticationCallback promptCallback =
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 82b34c3..73baf79 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -223,7 +223,7 @@
public Context getSettingsContext(int displayId) {
if (mSettingsContext == null || mSettingsContext.getDisplayId() != displayId) {
final Context systemUiContext = ActivityThread.currentActivityThread()
- .getSystemUiContext(displayId);
+ .createSystemUiContext(displayId);
final Context windowContext = systemUiContext.createWindowContext(
WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, null /* options */);
mSettingsContext = new ContextThemeWrapper(
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 607218e..b424c20 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -146,6 +146,12 @@
}
@Override
+ public boolean canHandleVolumeKey() {
+ // TODO: Implement when MediaSession2 starts to get key events.
+ return false;
+ }
+
+ @Override
public int getSessionPolicies() {
synchronized (mLock) {
return mPolicies;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 1525cd4..e4ed0e5 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -26,7 +26,9 @@
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.MediaMetadata;
+import android.media.MediaRouter2Manager;
import android.media.Rating;
+import android.media.RoutingSessionInfo;
import android.media.VolumeProvider;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
@@ -50,6 +52,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
+import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
@@ -121,6 +124,7 @@
private final SessionCb mSessionCb;
private final MediaSessionService mService;
private final Context mContext;
+ private final boolean mVolumeAdjustmentForRemoteGroupSessions;
private final Object mLock = new Object();
private final CopyOnWriteArrayList<ISessionControllerCallbackHolder>
@@ -180,6 +184,8 @@
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mAudioAttrs = DEFAULT_ATTRIBUTES;
mPolicies = policies;
+ mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
// May throw RemoteException if the session app is killed.
mSessionCb.mCb.asBinder().linkToDeath(this, 0);
@@ -449,6 +455,33 @@
}
@Override
+ public boolean canHandleVolumeKey() {
+ if (isPlaybackTypeLocal() || mVolumeAdjustmentForRemoteGroupSessions) {
+ return true;
+ }
+ MediaRouter2Manager mRouter2Manager = MediaRouter2Manager.getInstance(mContext);
+ List<RoutingSessionInfo> sessions =
+ mRouter2Manager.getRoutingSessions(mPackageName);
+ boolean foundNonSystemSession = false;
+ boolean isGroup = false;
+ for (RoutingSessionInfo session : sessions) {
+ if (!session.isSystemSession()) {
+ foundNonSystemSession = true;
+ int selectedRouteCount = session.getSelectedRoutes().size();
+ if (selectedRouteCount > 1) {
+ isGroup = true;
+ break;
+ }
+ }
+ }
+ if (!foundNonSystemSession) {
+ Log.d(TAG, "No routing session for " + mPackageName);
+ return false;
+ }
+ return !isGroup;
+ }
+
+ @Override
public int getSessionPolicies() {
synchronized (mLock) {
return mPolicies;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 3c50597..8f01f02 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -131,6 +131,13 @@
KeyEvent ke, int sequenceId, ResultReceiver cb);
/**
+ * Returns whether the media session can handle volume key events.
+ *
+ * @return True if this media session can handle volume key events, false otherwise.
+ */
+ boolean canHandleVolumeKey();
+
+ /**
* Get session policies from custom policy provider set when MediaSessionRecord is instantiated.
* If custom policy does not exist, will return null.
*/
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index c4c21df..b75ba75 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -325,8 +325,7 @@
int size = records.size();
for (int i = 0; i < size; i++) {
MediaSessionRecord record = records.get(i);
- // Do not send the volume key events to remote sessions.
- if (record.checkPlaybackActiveState(true) && record.isPlaybackTypeLocal()) {
+ if (record.checkPlaybackActiveState(true) && record.canHandleVolumeKey()) {
mCachedVolumeDefault = record;
return record;
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index a51ed09..2f353d1 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.wallpaper;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.WallpaperManager.COMMAND_REAPPLY;
import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
@@ -791,6 +792,7 @@
private final Context mContext;
private final WindowManagerInternal mWindowManagerInternal;
private final IPackageManager mIPackageManager;
+ private final ActivityManager mActivityManager;
private final MyPackageMonitor mMonitor;
private final AppOpsManager mAppOpsManager;
@@ -939,6 +941,11 @@
*/
WallpaperColors primaryColors;
+ /**
+ * If the wallpaper was set from a foreground app (instead of from a background service).
+ */
+ public boolean fromForegroundApp;
+
WallpaperConnection connection;
long lastDiedTime;
boolean wallpaperUpdating;
@@ -1688,6 +1695,7 @@
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */);
+ mActivityManager = mContext.getSystemService(ActivityManager.class);
mMonitor = new MyPackageMonitor();
mColorsChangedListeners = new SparseArray<>();
@@ -2648,6 +2656,9 @@
}
}
+ final boolean fromForegroundApp = Binder.withCleanCallingIdentity(() ->
+ mActivityManager.getPackageImportance(callingPackage) == IMPORTANCE_FOREGROUND);
+
synchronized (mLock) {
if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
WallpaperData wallpaper;
@@ -2670,6 +2681,7 @@
wallpaper.imageWallpaperPending = true;
wallpaper.whichPending = which;
wallpaper.setComplete = completion;
+ wallpaper.fromForegroundApp = fromForegroundApp;
wallpaper.cropHint.set(cropHint);
wallpaper.allowBackup = allowBackup;
}
@@ -3052,6 +3064,7 @@
wallpaper.callbacks.finishBroadcast();
final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
+ intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, wallpaper.fromForegroundApp);
mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index cf9783f..38a48570 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -20,11 +20,11 @@
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK;
import static android.os.Build.IS_USER;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY;
import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER;
@@ -1009,6 +1009,8 @@
final int windowType = windowState.mAttrs.type;
if (isExcludedWindowType(windowType)
|| ((windowState.mAttrs.privateFlags
+ & PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION) != 0)
+ || ((windowState.mAttrs.privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) {
continue;
}
@@ -1073,7 +1075,6 @@
}
}
}
-
visibleWindows.clear();
mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
@@ -1110,9 +1111,6 @@
private boolean isExcludedWindowType(int windowType) {
return windowType == TYPE_MAGNIFICATION_OVERLAY
- // Omit the touch region to avoid the cut out of the magnification
- // bounds because nav bar panel is unmagnifiable.
- || windowType == TYPE_NAVIGATION_BAR_PANEL
// Omit the touch region of window magnification to avoid the cut out of the
// magnification and the magnified center of window magnification could be
// in the bounds
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index d137436..f878562 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -770,10 +770,6 @@
if (compatStateInfo.mLastLoggedActivity == r) {
compatStateInfo.mLastLoggedActivity = null;
}
- if (compatStateInfo.mVisibleActivities.isEmpty()) {
- // No need to keep the entry if there are no visible activities.
- mPackageUidToCompatStateInfo.remove(packageUid);
- }
}
/**
@@ -1269,13 +1265,14 @@
* activity.
* <li>If the current state is NOT_VISIBLE, there is a previously logged state for the
* package UID and there are no other visible activities with the same package UID.
- * <li>The last logged activity with the same package UID is either {@code activity} or the
- * last logged state is NOT_VISIBLE or NOT_LETTERBOXED.
+ * <li>The last logged activity with the same package UID is either {@code activity} (or an
+ * activity that has been removed) or the last logged state is NOT_VISIBLE or NOT_LETTERBOXED.
* </ul>
*
* <p>If the current state is NOT_VISIBLE and the previous state which was logged by {@code
- * activity} wasn't, looks for the first visible activity with the same package UID that has
- * a letterboxed state, or a non-letterboxed state if there isn't one, and logs that state.
+ * activity} (or an activity that has been removed) wasn't, looks for the first visible activity
+ * with the same package UID that has a letterboxed state, or a non-letterboxed state if
+ * there isn't one, and logs that state.
*
* <p>This method assumes that the caller is wrapping the call with a synchronized block so
* that there won't be a race condition between two activities with the same package.
@@ -1311,14 +1308,14 @@
if (!isVisible && !visibleActivities.isEmpty()) {
// There is another visible activity for this package UID.
- if (activity == lastLoggedActivity) {
+ if (lastLoggedActivity == null || activity == lastLoggedActivity) {
// Make sure a new visible state is logged if needed.
findAppCompatStateToLog(compatStateInfo, packageUid);
}
return;
}
- if (activity != lastLoggedActivity
+ if (lastLoggedActivity != null && activity != lastLoggedActivity
&& lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE
&& lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED) {
// Another visible activity for this package UID has logged a letterboxed state.
@@ -1332,15 +1329,25 @@
* Looks for the first visible activity in {@code compatStateInfo} that has a letterboxed
* state, or a non-letterboxed state if there isn't one, and logs that state for the given
* {@code packageUid}.
+ *
+ * <p>If there is a visible activity in {@code compatStateInfo} with the same state as the
+ * last logged state for the given {@code packageUid}, changes the last logged activity to
+ * reference the first such activity without actually logging the same state twice.
*/
private void findAppCompatStateToLog(PackageCompatStateInfo compatStateInfo, int packageUid) {
final ArrayList<ActivityRecord> visibleActivities = compatStateInfo.mVisibleActivities;
+ final int lastLoggedState = compatStateInfo.mLastLoggedState;
ActivityRecord activityToLog = null;
int stateToLog = APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
for (int i = 0; i < visibleActivities.size(); i++) {
ActivityRecord activity = visibleActivities.get(i);
int state = activity.getAppCompatState();
+ if (state == lastLoggedState) {
+ // Change last logged activity without logging the same state twice.
+ compatStateInfo.mLastLoggedActivity = activity;
+ return;
+ }
if (state == APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE) {
// This shouldn't happen.
Slog.w(TAG, "Visible activity with NOT_VISIBLE App Compat state for package UID: "
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 535a061..f947773 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -934,6 +934,10 @@
voiceInteraction);
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
+ final RecentsAnimationController rac = mService.getRecentsAnimationController();
+ if (rac != null) {
+ rac.sendTasksAppeared();
+ }
for (int i = 0; i < openingApps.size(); ++i) {
openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9ef3ef2..29b1a80 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -34,7 +34,6 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Build.VERSION_CODES.N;
-import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.util.RotationUtils.deltaRotation;
@@ -63,7 +62,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
-import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
@@ -4998,12 +4996,6 @@
reconfigureDisplayLocked();
onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
mWmService.mDisplayNotificationController.dispatchDisplayAdded(this);
- // Attach the SystemUiContext to this DisplayContent the get latest configuration.
- // Note that the SystemUiContext will be removed automatically if this DisplayContent
- // is detached.
- mWmService.mWindowContextListenerController.registerWindowContainerListener(
- getDisplayUiContext().getWindowContextToken(), this, SYSTEM_UID,
- INVALID_WINDOW_TYPE, null /* options */);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 1d3c56e..296bd78 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -112,6 +112,9 @@
import android.annotation.Nullable;
import android.annotation.Px;
import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.LoadedApk;
+import android.app.ResourcesManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -448,7 +451,7 @@
: service.mContext.createDisplayContext(displayContent.getDisplay());
mUiContext = displayContent.isDefaultDisplay ? service.mAtmService.mUiContext
: service.mAtmService.mSystemThread
- .getSystemUiContext(displayContent.getDisplayId());
+ .createSystemUiContext(displayContent.getDisplayId());
mDisplayContent = displayContent;
mLock = service.getWindowManagerLock();
@@ -2254,8 +2257,19 @@
// For non-system users, ensure that the resources are loaded from the current
// user's package info (see ContextImpl.createDisplayContext)
- mCurrentUserResources = uiContext.createContextAsUser(UserHandle.of(userId), 0 /* flags*/)
- .getResources();
+ final LoadedApk pi = ActivityThread.currentActivityThread().getPackageInfo(
+ uiContext.getPackageName(), null, 0, userId);
+ mCurrentUserResources = ResourcesManager.getInstance().getResources(null,
+ pi.getResDir(),
+ null /* splitResDirs */,
+ pi.getOverlayDirs(),
+ pi.getOverlayPaths(),
+ pi.getApplicationInfo().sharedLibraryFiles,
+ mDisplayContent.getDisplayId(),
+ null /* overrideConfig */,
+ uiContext.getResources().getCompatibilityInfo(),
+ null /* classLoader */,
+ null /* loaders */);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 081a53e..fe21e5f 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -16,10 +16,8 @@
package com.android.server.wm;
-import static android.app.UiModeManager.MODE_NIGHT_AUTO;
-import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
-
import android.annotation.NonNull;
+import android.content.res.Configuration;
import android.os.Environment;
import android.os.LocaleList;
import android.util.AtomicFile;
@@ -303,7 +301,7 @@
}
boolean isResetNightMode() {
- return mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM;
+ return mNightMode == Configuration.UI_MODE_NIGHT_UNDEFINED;
}
@Override
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index b4963c5..b54208d 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -173,10 +172,8 @@
* to avoid flickering when running PiP animation across different orientations.
*/
void deferOrientationChangeForEnteringPipFromFullScreenIfNeeded() {
- final Task topFullscreenTask = mDisplayContent.getDefaultTaskDisplayArea()
- .getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN);
- final ActivityRecord topFullscreen = topFullscreenTask != null
- ? topFullscreenTask.topRunningActivity() : null;
+ final ActivityRecord topFullscreen = mDisplayContent.getActivity(
+ a -> a.fillsParent() && !a.getTask().inMultiWindowMode());
if (topFullscreen == null || topFullscreen.hasFixedRotationTransform()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 2057b1c..fd4b63e 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -163,6 +164,8 @@
private boolean mNavigationBarAttachedToApp;
private ActivityRecord mNavBarAttachedApp;
+ private final ArrayList<RemoteAnimationTarget> mPendingTaskAppears = new ArrayList<>();
+
/**
* An app transition listener to cancel the recents animation only after the app transition
* starts or is canceled.
@@ -732,11 +735,19 @@
return;
}
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addTaskToTargets, target: %s", target);
- try {
- mRunner.onTaskAppeared(target);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to report task appeared", e);
- }
+ mPendingTaskAppears.add(target);
+ }
+ }
+
+ void sendTasksAppeared() {
+ if (mPendingTaskAppears.isEmpty() || mRunner == null) return;
+ try {
+ final RemoteAnimationTarget[] targets = mPendingTaskAppears.toArray(
+ new RemoteAnimationTarget[0]);
+ mRunner.onTasksAppeared(targets);
+ mPendingTaskAppears.clear();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report task appeared", e);
}
}
@@ -744,10 +755,15 @@
OnAnimationFinishedCallback finishedCallback) {
final SparseBooleanArray recentTaskIds =
mService.mAtmService.getRecentTasks().getRecentTaskIds();
+ // The target must be built off the root task (the leaf task surface would be cropped
+ // within the root surface). However, recents only tracks leaf task ids, so we'll replace
+ // the task-id with the leaf id.
+ final Task leafTask = task.getTopLeafTask();
+ int taskId = leafTask.mTaskId;
TaskAnimationAdapter adapter = (TaskAnimationAdapter) addAnimation(task,
- !recentTaskIds.get(task.mTaskId), true /* hidden */, finishedCallback);
- mPendingNewTaskTargets.add(task.mTaskId);
- return adapter.createRemoteAnimationTarget();
+ !recentTaskIds.get(taskId), true /* hidden */, finishedCallback);
+ mPendingNewTaskTargets.add(taskId);
+ return adapter.createRemoteAnimationTarget(taskId);
}
void logRecentsAnimationStartTime(int durationMs) {
@@ -782,7 +798,8 @@
final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
- final RemoteAnimationTarget target = taskAdapter.createRemoteAnimationTarget();
+ final RemoteAnimationTarget target =
+ taskAdapter.createRemoteAnimationTarget(INVALID_TASK_ID);
if (target != null) {
targets.add(target);
} else {
@@ -995,6 +1012,8 @@
removeAnimation(taskAdapter);
taskAdapter.onCleanup();
}
+ // Should already be empty, but clean-up pending task-appears in-case they weren't sent.
+ mPendingTaskAppears.clear();
for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
final WallpaperAnimationAdapter wallpaperAdapter = mPendingWallpaperAnimations.get(i);
@@ -1224,7 +1243,14 @@
mLocalBounds.offsetTo(tmpPos.x, tmpPos.y);
}
- RemoteAnimationTarget createRemoteAnimationTarget() {
+ /**
+ * @param overrideTaskId overrides the target's taskId. It may differ from mTaskId and thus
+ * can differ from taskInfo. This mismatch is needed, however, in
+ * some cases where we are animating root tasks but need need leaf
+ * ids for identification. If this is INVALID (-1), then mTaskId
+ * will be used.
+ */
+ RemoteAnimationTarget createRemoteAnimationTarget(int overrideTaskId) {
final ActivityRecord topApp = mTask.getTopVisibleActivity();
final WindowState mainWindow = topApp != null
? topApp.findMainWindow()
@@ -1238,7 +1264,10 @@
final int mode = topApp.getActivityType() == mTargetActivityType
? MODE_OPENING
: MODE_CLOSING;
- mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash,
+ if (overrideTaskId < 0) {
+ overrideTaskId = mTask.mTaskId;
+ }
+ mTarget = new RemoteAnimationTarget(overrideTaskId, mode, mCapturedLeash,
!topApp.fillsParent(), new Rect(),
insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top),
mLocalBounds, mBounds, mTask.getWindowConfiguration(),
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 3ffa62d..b1eca9d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2585,7 +2585,7 @@
// starts. Instead, we expect home activities to be launched when the system is ready
// (ActivityManagerService#systemReady).
if (mService.isBooted() || mService.isBooting()) {
- startSystemDecorations(display);
+ startSystemDecorations(display.mDisplayContent);
}
// Drop any cached DisplayInfos associated with this display id - the values are now
// out of date given this display added event.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a799e7c..1b7a012 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1126,7 +1126,11 @@
if (inMultiWindowMode() || !hasChild()) return false;
if (intent != null) {
final int returnHomeFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME;
- return intent != null && (intent.getFlags() & returnHomeFlags) == returnHomeFlags;
+ final Task task = getDisplayArea() != null ? getDisplayArea().getRootHomeTask() : null;
+ final boolean isLockTaskModeViolation = task != null
+ && mAtmService.getLockTaskController().isLockTaskModeViolation(task);
+ return (intent.getFlags() & returnHomeFlags) == returnHomeFlags
+ && !isLockTaskModeViolation;
}
final Task bottomTask = getBottomMostTask();
return bottomTask != this && bottomTask.returnsToHomeRootTask();
@@ -2377,6 +2381,16 @@
return true;
}
+ /** Return the top-most leaf-task under this one, or this task if it is a leaf. */
+ public Task getTopLeafTask() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final Task child = mChildren.get(i).asTask();
+ if (child == null) continue;
+ return child.getTopLeafTask();
+ }
+ return this;
+ }
+
int getDescendantTaskCount() {
final int[] currentCount = {0};
final PooledConsumer c = PooledLambda.obtainConsumer((t, count) -> { count[0]++; },
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index 86e356a..bc53041 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -45,7 +45,7 @@
*
* <ul>
* <li>When a {@link WindowContext} is created, it registers the listener via
- * {@link WindowManagerService#attachWindowContextToDisplayArea(IBinder, int, int, Bundle)}
+ * {@link WindowManagerService#registerWindowContextListener(IBinder, int, int, Bundle)}
* automatically.</li>
* <li>When the {@link WindowContext} adds the first window to the screen via
* {@link android.view.WindowManager#addView(View, android.view.ViewGroup.LayoutParams)},
@@ -53,7 +53,7 @@
* to corresponding {@link WindowToken} via this controller.</li>
* <li>When the {@link WindowContext} is GCed, it unregisters the previously
* registered listener via
- * {@link WindowManagerService#detachWindowContextFromWindowContainer(IBinder)}.
+ * {@link WindowManagerService#unregisterWindowContextListener(IBinder)}.
* {@link WindowManagerService} is also responsible for removing the
* {@link WindowContext} created {@link WindowToken}.</li>
* </ul>
@@ -68,7 +68,7 @@
/**
* Registers the listener to a {@code container} which is associated with
- * a {@code clientToken}, which is a {@link android.window.WindowContext} representation. If the
+ * a {@code clientToken}, which is a {@link android.app.WindowContext} representation. If the
* listener associated with {@code clientToken} hasn't been initialized yet, create one
* {@link WindowContextListenerImpl}. Otherwise, the listener associated with
* {@code clientToken} switches to listen to the {@code container}.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3edcd5c..9bede97 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2741,9 +2741,6 @@
@Override
public Configuration attachWindowContextToDisplayArea(IBinder clientToken, int
type, int displayId, Bundle options) {
- if (clientToken == null) {
- throw new IllegalArgumentException("clientToken must not be null!");
- }
final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
"attachWindowContextToDisplayArea", false /* printLog */);
final int callingUid = Binder.getCallingUid();
@@ -2834,39 +2831,6 @@
}
}
- @Override
- public Configuration attachToDisplayContent(IBinder clientToken, int displayId) {
- if (clientToken == null) {
- throw new IllegalArgumentException("clientToken must not be null!");
- }
- final int callingUid = Binder.getCallingUid();
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- // We use "getDisplayContent" instead of "getDisplayContentOrCreate" because
- // this method may be called in DisplayPolicy's constructor and may cause
- // infinite loop. In this scenario, we early return here and switch to do the
- // registration in DisplayContent#onParentChanged at DisplayContent initialization.
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc == null) {
- if (Binder.getCallingPid() != myPid()) {
- throw new WindowManager.InvalidDisplayException("attachToDisplayContent: "
- + "trying to attach to a non-existing display:" + displayId);
- }
- // Early return if this method is invoked from system process.
- // See above comments for more detail.
- return null;
- }
-
- mWindowContextListenerController.registerWindowContainerListener(clientToken, dc,
- callingUid, INVALID_WINDOW_TYPE, null /* options */);
- return dc.getConfiguration();
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
/** Returns {@code true} if this binder is a registered window token. */
@Override
public boolean isWindowToken(IBinder binder) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 5dd01a5..bd8d116 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -581,115 +581,18 @@
task.getDisplayArea().setLaunchAdjacentFlagRootTask(clearRoot ? null : task);
break;
}
- case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
+ case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: {
effects |= setAdjacentRootsHierarchyOp(hop);
break;
- }
- // The following operations may change task order so they are skipped while in lock task
- // mode. The above operations are still allowed because they don't move tasks. And it may
- // be necessary such as clearing launch root after entering lock task mode.
- if (isInLockTaskMode) {
- Slog.w(TAG, "Skip applying hierarchy operation " + hop + " while in lock task mode");
- return effects;
- }
-
- final WindowContainer wc;
- final IBinder fragmentToken;
- switch (type) {
- case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
- effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
- break;
- case HIERARCHY_OP_TYPE_REORDER:
- case HIERARCHY_OP_TYPE_REPARENT:
- wc = WindowContainer.fromBinder(hop.getContainer());
- if (wc == null || !wc.isAttached()) {
- Slog.e(TAG, "Attempt to operate on detached container: " + wc);
- break;
- }
- if (syncId >= 0) {
- addToSyncSet(syncId, wc);
- }
- if (transition != null) {
- transition.collect(wc);
- if (hop.isReparent()) {
- if (wc.getParent() != null) {
- // Collect the current parent. It's visibility may change as
- // a result of this reparenting.
- transition.collect(wc.getParent());
- }
- if (hop.getNewParent() != null) {
- final WindowContainer parentWc =
- WindowContainer.fromBinder(hop.getNewParent());
- if (parentWc == null) {
- Slog.e(TAG, "Can't resolve parent window from token");
- break;
- }
- transition.collect(parentWc);
- }
- }
- }
- effects |= sanitizeAndApplyHierarchyOp(wc, hop);
- break;
- case HIERARCHY_OP_TYPE_LAUNCH_TASK:
- mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
- "launchTask HierarchyOp");
- final Bundle launchOpts = hop.getLaunchOptions();
- final int taskId = launchOpts.getInt(
- WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
- launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
- final SafeActivityOptions safeOptions =
- SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid);
- final Integer[] starterResult = { null };
- // startActivityFromRecents should not be called in lock.
- mService.mH.post(() -> {
- try {
- starterResult[0] = mService.mTaskSupervisor.startActivityFromRecents(
- caller.mPid, caller.mUid, taskId, safeOptions);
- } catch (Throwable t) {
- starterResult[0] = ActivityManager.START_CANCELED;
- Slog.w(TAG, t);
- }
- synchronized (mGlobalLock) {
- mGlobalLock.notifyAll();
- }
- });
- while (starterResult[0] == null) {
- try {
- mGlobalLock.wait();
- } catch (InterruptedException ignored) {
- }
- }
- break;
- case HIERARCHY_OP_TYPE_PENDING_INTENT:
- String resolvedType = hop.getActivityIntent() != null
- ? hop.getActivityIntent().resolveTypeIfNeeded(
- mService.mContext.getContentResolver())
- : null;
-
- Bundle options = null;
- if (hop.getPendingIntent().isActivity()) {
- // Set the context display id as preferred for this activity launches, so that
- // it can land on caller's display. Or just brought the task to front at the
- // display where it was on since it has higher preference.
- ActivityOptions activityOptions = hop.getLaunchOptions() != null
- ? new ActivityOptions(hop.getLaunchOptions())
- : ActivityOptions.makeBasic();
- activityOptions.setCallerDisplayId(DEFAULT_DISPLAY);
- options = activityOptions.toBundle();
- }
-
- mService.mAmInternal.sendIntentSender(hop.getPendingIntent().getTarget(),
- hop.getPendingIntent().getWhitelistToken(), 0 /* code */,
- hop.getActivityIntent(), resolvedType, null /* finishReceiver */,
- null /* requiredPermission */, options);
- break;
- case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
+ }
+ case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: {
final TaskFragmentCreationParams taskFragmentCreationOptions =
hop.getTaskFragmentCreationOptions();
createTaskFragment(taskFragmentCreationOptions, errorCallbackToken);
break;
- case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
- wc = WindowContainer.fromBinder(hop.getContainer());
+ }
+ case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: {
+ final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
if (wc == null || !wc.isAttached()) {
Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc);
break;
@@ -699,10 +602,24 @@
throw new IllegalArgumentException(
"Can only delete organized TaskFragment, but not Task.");
}
+ if (isInLockTaskMode) {
+ final ActivityRecord bottomActivity = taskFragment.getActivity(
+ a -> !a.finishing, false /* traverseTopToBottom */);
+ if (bottomActivity != null
+ && mService.getLockTaskController().activityBlockedFromFinish(
+ bottomActivity)) {
+ Slog.w(TAG, "Skip removing TaskFragment due in lock task mode.");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken,
+ new IllegalStateException(
+ "Not allow to delete task fragment in lock task mode."));
+ break;
+ }
+ }
effects |= deleteTaskFragment(taskFragment, errorCallbackToken);
break;
- case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
- fragmentToken = hop.getContainer();
+ }
+ case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
+ final IBinder fragmentToken = hop.getContainer();
if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
final Throwable exception = new IllegalArgumentException(
"Not allowed to operate with invalid fragment token");
@@ -721,8 +638,9 @@
convertStartFailureToThrowable(result, activityIntent));
}
break;
- case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
- fragmentToken = hop.getNewParent();
+ }
+ case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: {
+ final IBinder fragmentToken = hop.getNewParent();
final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) {
final Throwable exception = new IllegalArgumentException(
@@ -733,21 +651,9 @@
activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
- case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
- final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
- final WindowContainer newParent = hop.getNewParent() != null
- ? WindowContainer.fromBinder(hop.getNewParent())
- : null;
- if (oldParent == null || !oldParent.isAttached()) {
- Slog.e(TAG, "Attempt to operate on unknown or detached container: "
- + oldParent);
- break;
- }
- reparentTaskFragment(oldParent, newParent, errorCallbackToken);
- effects |= TRANSACT_EFFECTS_LIFECYCLE;
- break;
- case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
- fragmentToken = hop.getContainer();
+ }
+ case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS: {
+ final IBinder fragmentToken = hop.getContainer();
final IBinder adjacentFragmentToken = hop.getAdjacentRoot();
final TaskFragment tf1 = mLaunchTaskFragments.get(fragmentToken);
final TaskFragment tf2 = adjacentFragmentToken != null
@@ -777,6 +683,126 @@
adjacentParams.shouldDelaySecondaryLastActivityRemoval());
}
break;
+ }
+ default: {
+ // The other operations may change task order so they are skipped while in lock
+ // task mode. The above operations are still allowed because they don't move
+ // tasks. And it may be necessary such as clearing launch root after entering
+ // lock task mode.
+ if (isInLockTaskMode) {
+ Slog.w(TAG, "Skip applying hierarchy operation " + hop
+ + " while in lock task mode");
+ return effects;
+ }
+ }
+ }
+
+ switch (type) {
+ case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: {
+ effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
+ break;
+ }
+ case HIERARCHY_OP_TYPE_REORDER:
+ case HIERARCHY_OP_TYPE_REPARENT: {
+ final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+ if (wc == null || !wc.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on detached container: " + wc);
+ break;
+ }
+ if (syncId >= 0) {
+ addToSyncSet(syncId, wc);
+ }
+ if (transition != null) {
+ transition.collect(wc);
+ if (hop.isReparent()) {
+ if (wc.getParent() != null) {
+ // Collect the current parent. It's visibility may change as
+ // a result of this reparenting.
+ transition.collect(wc.getParent());
+ }
+ if (hop.getNewParent() != null) {
+ final WindowContainer parentWc =
+ WindowContainer.fromBinder(hop.getNewParent());
+ if (parentWc == null) {
+ Slog.e(TAG, "Can't resolve parent window from token");
+ break;
+ }
+ transition.collect(parentWc);
+ }
+ }
+ }
+ effects |= sanitizeAndApplyHierarchyOp(wc, hop);
+ break;
+ }
+ case HIERARCHY_OP_TYPE_LAUNCH_TASK: {
+ mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
+ "launchTask HierarchyOp");
+ final Bundle launchOpts = hop.getLaunchOptions();
+ final int taskId = launchOpts.getInt(
+ WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
+ launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
+ final SafeActivityOptions safeOptions =
+ SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid);
+ final Integer[] starterResult = {null};
+ // startActivityFromRecents should not be called in lock.
+ mService.mH.post(() -> {
+ try {
+ starterResult[0] = mService.mTaskSupervisor.startActivityFromRecents(
+ caller.mPid, caller.mUid, taskId, safeOptions);
+ } catch (Throwable t) {
+ starterResult[0] = ActivityManager.START_CANCELED;
+ Slog.w(TAG, t);
+ }
+ synchronized (mGlobalLock) {
+ mGlobalLock.notifyAll();
+ }
+ });
+ while (starterResult[0] == null) {
+ try {
+ mGlobalLock.wait();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ break;
+ }
+ case HIERARCHY_OP_TYPE_PENDING_INTENT: {
+ String resolvedType = hop.getActivityIntent() != null
+ ? hop.getActivityIntent().resolveTypeIfNeeded(
+ mService.mContext.getContentResolver())
+ : null;
+
+ Bundle options = null;
+ if (hop.getPendingIntent().isActivity()) {
+ // Set the context display id as preferred for this activity launches, so that
+ // it can land on caller's display. Or just brought the task to front at the
+ // display where it was on since it has higher preference.
+ ActivityOptions activityOptions = hop.getLaunchOptions() != null
+ ? new ActivityOptions(hop.getLaunchOptions())
+ : ActivityOptions.makeBasic();
+ activityOptions.setCallerDisplayId(DEFAULT_DISPLAY);
+ options = activityOptions.toBundle();
+ }
+
+ mService.mAmInternal.sendIntentSender(hop.getPendingIntent().getTarget(),
+ hop.getPendingIntent().getWhitelistToken(), 0 /* code */,
+ hop.getActivityIntent(), resolvedType, null /* finishReceiver */,
+ null /* requiredPermission */, options);
+ break;
+ }
+ case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: {
+ final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
+ final WindowContainer newParent = hop.getNewParent() != null
+ ? WindowContainer.fromBinder(hop.getNewParent())
+ : null;
+ if (oldParent == null || !oldParent.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: "
+ + oldParent);
+ break;
+ }
+ reparentTaskFragment(oldParent, newParent, errorCallbackToken);
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ break;
+ }
}
return effects;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8042841..db13ae2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -12752,6 +12752,13 @@
}
@Override
+ public @Nullable ComponentName getProfileOwnerOrDeviceOwnerSupervisionComponent(
+ @NonNull UserHandle userHandle) {
+ return DevicePolicyManagerService.this.getProfileOwnerOrDeviceOwnerSupervisionComponent(
+ userHandle);
+ }
+
+ @Override
public boolean isActiveDeviceOwner(int uid) {
return isDeviceOwner(new CallerIdentity(uid, null, null));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
index 24c58f4..7358551 100644
--- a/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
@@ -72,7 +72,7 @@
HashMap<String, Long> installedPkgs = new HashMap<>();
installedPkgs.put(TEST_PACKAGE_NAME, System.currentTimeMillis());
- mService.init(System.currentTimeMillis(), installedPkgs);
+ mService.init(System.currentTimeMillis(), installedPkgs, true);
}
@After
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 2a5bb18..df975cd 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -40,7 +40,6 @@
import android.os.IBinder;
import android.os.UserHandle;
import android.provider.Settings;
-import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -54,13 +53,16 @@
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* APCT tests for {@link AccessibilityManagerService}.
*/
-public class AccessibilityManagerServiceTest extends AndroidTestCase {
+public class AccessibilityManagerServiceTest {
private static final String TAG = "A11Y_MANAGER_SERVICE_TEST";
private static final int ACTION_ID = 20;
private static final String LABEL = "label";
@@ -104,8 +106,8 @@
private AccessibilityServiceConnection mAccessibilityServiceConnection;
private AccessibilityManagerService mA11yms;
- @Override
- protected void setUp() throws Exception {
+ @Before
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
@@ -167,44 +169,48 @@
}
@SmallTest
+ @Test
public void testRegisterSystemActionWithoutPermission() throws Exception {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
try {
mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
- fail();
+ Assert.fail();
} catch (SecurityException expected) {
}
verify(mMockSystemActionPerformer, never()).registerSystemAction(ACTION_ID, TEST_ACTION);
}
@SmallTest
+ @Test
public void testRegisterSystemAction() throws Exception {
mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
verify(mMockSystemActionPerformer).registerSystemAction(ACTION_ID, TEST_ACTION);
}
- @SmallTest
+ @Test
public void testUnregisterSystemActionWithoutPermission() throws Exception {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
try {
mA11yms.unregisterSystemAction(ACTION_ID);
- fail();
+ Assert.fail();
} catch (SecurityException expected) {
}
verify(mMockSystemActionPerformer, never()).unregisterSystemAction(ACTION_ID);
}
@SmallTest
+ @Test
public void testUnregisterSystemAction() throws Exception {
mA11yms.unregisterSystemAction(ACTION_ID);
verify(mMockSystemActionPerformer).unregisterSystemAction(ACTION_ID);
}
@SmallTest
+ @Test
public void testOnSystemActionsChanged() throws Exception {
setupAccessibilityServiceConnection();
mA11yms.notifySystemActionsChangedLocked(mUserState);
@@ -213,6 +219,7 @@
}
@SmallTest
+ @Test
public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() {
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
@@ -223,7 +230,7 @@
mA11yms.onMagnificationTransitionEndedLocked(false);
- assertEquals(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
+ Assert.assertEquals(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
userState.getMagnificationModeLocked());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index d7daa57..a8ede13 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -29,9 +29,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import android.app.ActivityThread;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -46,7 +44,6 @@
import com.android.server.inputmethod.InputMethodManagerService;
import com.android.server.inputmethod.InputMethodMenuController;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -65,9 +62,6 @@
private InputMethodMenuController mController;
private DualDisplayAreaGroupPolicyTest.DualDisplayContent mSecondaryDisplay;
- private IWindowManager mIWindowManager;
- private DisplayManagerGlobal mDisplayManagerGlobal;
-
@Before
public void setUp() throws Exception {
// Let the Display to be created with the DualDisplay policy.
@@ -76,12 +70,10 @@
Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
mController = new InputMethodMenuController(mock(InputMethodManagerService.class));
- mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
- .Builder(mAtm, 1000, 1000).build();
// Mock addWindowTokenWithOptions to create a test window token.
- mIWindowManager = WindowManagerGlobal.getWindowManagerService();
- spyOn(mIWindowManager);
+ IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+ spyOn(wms);
doAnswer(invocation -> {
Object[] args = invocation.getArguments();
IBinder clientToken = (IBinder) args[0];
@@ -91,24 +83,19 @@
dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
null /* options */);
return dc.getImeContainer().getConfiguration();
- }).when(mIWindowManager).attachWindowContextToDisplayArea(any(),
- eq(TYPE_INPUT_METHOD_DIALOG), anyInt(), any());
- mDisplayManagerGlobal = DisplayManagerGlobal.getInstance();
- spyOn(mDisplayManagerGlobal);
+ }).when(wms).attachWindowContextToDisplayArea(any(), eq(TYPE_INPUT_METHOD_DIALOG),
+ anyInt(), any());
+
+ mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
+ .Builder(mAtm, 1000, 1000).build();
+
+ // Mock DisplayManagerGlobal to return test display when obtaining Display instance.
final int displayId = mSecondaryDisplay.getDisplayId();
final Display display = mSecondaryDisplay.getDisplay();
- doReturn(display).when(mDisplayManagerGlobal).getCompatibleDisplay(eq(displayId),
+ DisplayManagerGlobal displayManagerGlobal = DisplayManagerGlobal.getInstance();
+ spyOn(displayManagerGlobal);
+ doReturn(display).when(displayManagerGlobal).getCompatibleDisplay(eq(displayId),
(Resources) any());
- Context systemUiContext = ActivityThread.currentActivityThread()
- .getSystemUiContext(displayId);
- spyOn(systemUiContext);
- doReturn(display).when(systemUiContext).getDisplay();
- }
-
- @After
- public void tearDown() {
- reset(mIWindowManager);
- reset(mDisplayManagerGlobal);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 9c04560..9639aa7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1084,6 +1084,7 @@
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioScreenOrientationNotSetThenChangedToPortrait() {
// In this test, the activity's orientation isn't fixed to portrait, therefore the override
@@ -1115,6 +1116,7 @@
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioScreenOrientationLandscapeThenChangedToPortrait() {
// In this test, the activity's orientation isn't fixed to portrait, therefore the override
@@ -1147,6 +1149,7 @@
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioScreenOrientationPortraitThenChangedToUnspecified() {
setUpDisplaySizeWithApp(1000, 1200);
@@ -1175,6 +1178,52 @@
}
@Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
+ public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationNotSet() {
+ setUpDisplaySizeWithApp(1000, 1200);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override forces the activity into a 3:2 aspect ratio
+ assertEquals(1200, activity.getBounds().height());
+ assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
+ public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationLandscape() {
+ // In this test, the activity's orientation isn't fixed to portrait, therefore the override
+ // isn't applied.
+
+ setUpDisplaySizeWithApp(1000, 1200);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override forces the activity into a 3:2 aspect ratio
+ assertEquals(1000 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ activity.getBounds().height(), 0.5);
+ assertEquals(1000, activity.getBounds().width());
+ }
+
+ @Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioWithoutGlobalOverride() {
// In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 1b84927..ac1fcce 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -380,6 +380,7 @@
if (userId == UserHandle.USER_SYSTEM) {
UsageStatsIdleService.scheduleUpdateMappingsJob(getContext());
}
+ final boolean deleteObsoleteData = shouldDeleteObsoleteData(UserHandle.of(userId));
synchronized (mLock) {
// This should be safe to add this early. Other than reportEventOrAddToQueue, every
// other user grabs the lock before accessing
@@ -402,7 +403,7 @@
boolean needToFlush = !pendingEvents.isEmpty();
initializeUserUsageStatsServiceLocked(userId, System.currentTimeMillis(),
- installedPackages);
+ installedPackages, deleteObsoleteData);
final UserUsageStatsService userService = getUserUsageStatsServiceLocked(userId);
if (userService == null) {
Slog.i(TAG, "Attempted to unlock stopped or removed user " + userId);
@@ -596,13 +597,13 @@
* when the user is initially unlocked.
*/
private void initializeUserUsageStatsServiceLocked(int userId, long currentTimeMillis,
- HashMap<String, Long> installedPackages) {
+ HashMap<String, Long> installedPackages, boolean deleteObsoleteData) {
final File usageStatsDir = new File(Environment.getDataSystemCeDirectory(userId),
"usagestats");
final UserUsageStatsService service = new UserUsageStatsService(getContext(), userId,
usageStatsDir, this);
try {
- service.init(currentTimeMillis, installedPackages);
+ service.init(currentTimeMillis, installedPackages, deleteObsoleteData);
mUserState.put(userId, service);
} catch (Exception e) {
if (mUserManager.isUserUnlocked(userId)) {
@@ -1165,6 +1166,10 @@
* Called by the Binder stub.
*/
private boolean updatePackageMappingsData() {
+ // don't update the mappings if a profile user is defined
+ if (!shouldDeleteObsoleteData(UserHandle.SYSTEM)) {
+ return true; // return true so job scheduler doesn't reschedule the job
+ }
// fetch the installed packages outside the lock so it doesn't block package manager.
final HashMap<String, Long> installedPkgs = getInstalledPackages(UserHandle.USER_SYSTEM);
synchronized (mLock) {
@@ -1309,6 +1314,13 @@
}
}
+ private boolean shouldDeleteObsoleteData(UserHandle userHandle) {
+ final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
+ // If a profile owner is not defined for the given user, obsolete data should be deleted
+ return dpmInternal == null
+ || dpmInternal.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle) == null;
+ }
+
private String buildFullToken(String packageName, String token) {
final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1);
sb.append(packageName);
@@ -2532,8 +2544,12 @@
private class MyPackageMonitor extends PackageMonitor {
@Override
public void onPackageRemoved(String packageName, int uid) {
- mHandler.obtainMessage(MSG_PACKAGE_REMOVED, getChangingUserId(), 0, packageName)
- .sendToTarget();
+ final int changingUserId = getChangingUserId();
+ // Only remove the package's data if a profile owner is not defined for the user
+ if (shouldDeleteObsoleteData(UserHandle.of(changingUserId))) {
+ mHandler.obtainMessage(MSG_PACKAGE_REMOVED, changingUserId, 0, packageName)
+ .sendToTarget();
+ }
super.onPackageRemoved(packageName, uid);
}
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 36d8c85..fee4a47 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -115,8 +115,9 @@
mSystemTimeSnapshot = System.currentTimeMillis();
}
- void init(final long currentTimeMillis, HashMap<String, Long> installedPackages) {
- readPackageMappingsLocked(installedPackages);
+ void init(final long currentTimeMillis, HashMap<String, Long> installedPackages,
+ boolean deleteObsoleteData) {
+ readPackageMappingsLocked(installedPackages, deleteObsoleteData);
mDatabase.init(currentTimeMillis);
if (mDatabase.wasUpgradePerformed()) {
mDatabase.prunePackagesDataOnUpgrade(installedPackages);
@@ -180,12 +181,13 @@
return mDatabase.onPackageRemoved(packageName, timeRemoved);
}
- private void readPackageMappingsLocked(HashMap<String, Long> installedPackages) {
+ private void readPackageMappingsLocked(HashMap<String, Long> installedPackages,
+ boolean deleteObsoleteData) {
mDatabase.readMappingsLocked();
// Package mappings for the system user are updated after 24 hours via a job scheduled by
// UsageStatsIdleService to ensure restored data is not lost on first boot. Additionally,
// this makes user service initialization a little quicker on subsequent boots.
- if (mUserId != UserHandle.USER_SYSTEM) {
+ if (mUserId != UserHandle.USER_SYSTEM && deleteObsoleteData) {
updatePackageMappingsLocked(installedPackages);
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 54d3af5..4d2e007 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3893,6 +3893,30 @@
public static final String KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL =
"enabled_4g_opportunistic_network_scan_bool";
+ /**
+ * Only relevant when the device supports opportunistic networks but does not support
+ * simultaneuous 5G+5G. Controls how long, in milliseconds, to wait before opportunistic network
+ * goes out of service before switching the 5G capability back to primary stack. The idea of
+ * waiting a few seconds is to minimize the calling of the expensive capability switching
+ * operation in the case where CBRS goes back into service shortly after going out of it.
+ *
+ * @hide
+ */
+ public static final String KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG =
+ "time_to_switch_back_to_primary_if_opportunistic_oos_long";
+
+ /**
+ * Only relevant when the device supports opportunistic networks but does not support
+ * simultaneuous 5G+5G. Controls how long, in milliseconds, after 5G capability has switched back
+ * to primary stack due to opportunistic network being OOS. The idea is to minimizing the
+ * 'ping-ponging' effect where device is constantly witching capability back and forth between
+ * primary and opportunistic stack.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG
+ = "opportunistic_time_to_scan_after_capability_switch_to_primary_long";
+
/**
* Indicates zero or more emergency number prefix(es), because some carrier requires
* if users dial an emergency number address with a specific prefix, the combination of the
@@ -5766,6 +5790,10 @@
/* Default value is 2 seconds. */
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000);
sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true);
+ sDefaults.putInt(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000);
+ sDefaults.putInt(
+ KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
+ 120000);
sDefaults.putAll(Gps.getDefaults());
sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
new int[] {