Merge "Create default implementation for getSyncTarget"
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 463d0c49..fccb3c0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1026,6 +1026,7 @@
}
public class Typeface {
+ method @NonNull public static android.util.Pair<java.util.List<android.graphics.Typeface>,java.util.List<android.graphics.Typeface>> changeDefaultFontForTest(@NonNull java.util.List<android.graphics.Typeface>, @NonNull java.util.List<android.graphics.Typeface>);
method @NonNull public static long[] deserializeFontMap(@NonNull java.nio.ByteBuffer, @NonNull java.util.Map<java.lang.String,android.graphics.Typeface>) throws java.io.IOException;
method @Nullable public static android.os.SharedMemory getSystemFontMapSharedMemory();
method @NonNull public static android.os.SharedMemory serializeFontMap(@NonNull java.util.Map<java.lang.String,android.graphics.Typeface>) throws android.system.ErrnoException, java.io.IOException;
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 91d6a9b..40f7533 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -117,6 +117,7 @@
/**
* Shuts down the service
*/
+ @EnforcePermission("SHUTDOWN")
void shutdown();
/**
@@ -277,6 +278,7 @@
*/
void setUidOnMeteredNetworkDenylist(int uid, boolean enable);
void setUidOnMeteredNetworkAllowlist(int uid, boolean enable);
+ @EnforcePermission("NETWORK_SETTINGS")
boolean setDataSaverModeEnabled(boolean enable);
void setUidCleartextNetworkPolicy(int uid, int policy);
@@ -308,5 +310,6 @@
void removeInterfaceFromLocalNetwork(String iface);
int removeRoutesFromLocalNetwork(in List<RouteInfo> routes);
+ @EnforcePermission("OBSERVE_NETWORK_POLICY")
boolean isNetworkRestricted(int uid);
}
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
index 8dbaec4..60edf35 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
@@ -139,11 +139,13 @@
@WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
@Nullable IRemoteInputConnection remoteInputConnection,
@Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
- int unverifiedTargetSdkVersion, @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
try {
return mTarget.startInputOrWindowGainedFocus(startInputReason, client, windowToken,
startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
- remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, imeDispatcher);
+ remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
+ imeDispatcher);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index aacd4ec..8ce134e7 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -285,9 +285,10 @@
private static final String SUBTYPE_MODE_VOICE = "voice";
/**
- * Provide this to {@link IInputMethodManager#startInputOrWindowGainedFocus(
- * int, IInputMethodClient, IBinder, int, int, int, EditorInfo,
- * com.android.internal.inputmethod.IRemoteInputConnection, int)} to receive
+ * Provide this to {@link IInputMethodManagerInvoker#startInputOrWindowGainedFocus(int,
+ * IInputMethodClient, IBinder, int, int, int, EditorInfo,
+ * com.android.internal.inputmethod.IRemoteInputConnection, IRemoteAccessibilityInputConnection,
+ * int, int, ImeOnBackInvokedDispatcher)} to receive
* {@link android.window.OnBackInvokedCallback} registrations from IME.
*/
private final ImeOnBackInvokedDispatcher mImeDispatcher =
@@ -790,7 +791,7 @@
null,
null, null,
mCurRootView.mContext.getApplicationInfo().targetSdkVersion,
- mImeDispatcher);
+ UserHandle.myUserId(), mImeDispatcher);
}
}
@@ -2331,22 +2332,22 @@
// Okay we are now ready to call into the served view and have it
// do its stuff.
// Life is good: let's hook everything up!
- EditorInfo tba = new EditorInfo();
+ EditorInfo editorInfo = new EditorInfo();
// Note: Use Context#getOpPackageName() rather than Context#getPackageName() so that the
// system can verify the consistency between the uid of this process and package name passed
// from here. See comment of Context#getOpPackageName() for details.
- tba.packageName = view.getContext().getOpPackageName();
- tba.autofillId = view.getAutofillId();
- tba.fieldId = view.getId();
- InputConnection ic = view.onCreateInputConnection(tba);
- if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
+ editorInfo.packageName = view.getContext().getOpPackageName();
+ editorInfo.autofillId = view.getAutofillId();
+ editorInfo.fieldId = view.getId();
+ InputConnection ic = view.onCreateInputConnection(editorInfo);
+ if (DEBUG) Log.v(TAG, "Starting input: editorInfo=" + editorInfo + " ic=" + ic);
// Clear autofill and field ids if a connection could not be established.
// This ensures that even disconnected EditorInfos have well-defined attributes,
// making them consistently and straightforwardly comparable.
if (ic == null) {
- tba.autofillId = AutofillId.NO_AUTOFILL_ID;
- tba.fieldId = 0;
+ editorInfo.autofillId = AutofillId.NO_AUTOFILL_ID;
+ editorInfo.fieldId = 0;
}
final Handler icHandler;
@@ -2378,7 +2379,7 @@
}
// Hook 'em up and let 'er rip.
- mCurrentEditorInfo = tba.createCopyInternal();
+ mCurrentEditorInfo = editorInfo.createCopyInternal();
// Store the previously served connection so that we can determine whether it is safe
// to skip the call to startInputOrWindowGainedFocus in the IMMS
final RemoteInputConnectionImpl previouslyServedConnection = mServedInputConnection;
@@ -2391,8 +2392,8 @@
}
final RemoteInputConnectionImpl servedInputConnection;
if (ic != null) {
- mCursorSelStart = tba.initialSelStart;
- mCursorSelEnd = tba.initialSelEnd;
+ mCursorSelStart = editorInfo.initialSelStart;
+ mCursorSelEnd = editorInfo.initialSelEnd;
mInitialSelStart = mCursorSelStart;
mInitialSelEnd = mCursorSelEnd;
mCursorCandStart = -1;
@@ -2418,7 +2419,7 @@
if (DEBUG) {
Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
- + ic + " tba=" + tba + " startInputFlags="
+ + ic + " editorInfo=" + editorInfo + " startInputFlags="
+ InputMethodDebug.startInputFlagsToString(startInputFlags));
}
@@ -2437,19 +2438,21 @@
}
return false;
}
+ final int targetUserId = editorInfo.targetInputMethodUser != null
+ ? editorInfo.targetInputMethodUser.getIdentifier() : UserHandle.myUserId();
res = mServiceInvoker.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, startInputFlags,
- softInputMode, windowFlags, tba, servedInputConnection,
+ softInputMode, windowFlags, editorInfo, servedInputConnection,
servedInputConnection == null ? null
: servedInputConnection.asIRemoteAccessibilityInputConnection(),
- view.getContext().getApplicationInfo().targetSdkVersion,
+ view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
mImeDispatcher);
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
if (res == null) {
Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
+ " null. startInputReason="
+ InputMethodDebug.startInputReasonToString(startInputReason)
- + " editorInfo=" + tba
+ + " editorInfo=" + editorInfo
+ " startInputFlags="
+ InputMethodDebug.startInputFlagsToString(startInputFlags));
return false;
@@ -2491,9 +2494,9 @@
if (ic != null && res != null && res.method != null) {
if (DEBUG) {
Log.v(TAG, "Calling View.onInputConnectionOpened: view= " + view
- + ", ic=" + ic + ", tba=" + tba + ", handler=" + icHandler);
+ + ", ic=" + ic + ", editorInfo=" + editorInfo + ", handler=" + icHandler);
}
- reportInputConnectionOpened(ic, tba, icHandler, view);
+ reportInputConnectionOpened(ic, editorInfo, icHandler, view);
}
return true;
@@ -2541,8 +2544,8 @@
}
private void reportInputConnectionOpened(
- InputConnection ic, EditorInfo tba, Handler icHandler, View view) {
- view.onInputConnectionOpenedInternal(ic, tba, icHandler);
+ InputConnection ic, EditorInfo editorInfo, Handler icHandler, View view) {
+ view.onInputConnectionOpenedInternal(ic, editorInfo, icHandler);
final ViewRootImpl viewRoot = view.getViewRootImpl();
if (viewRoot != null) {
viewRoot.getHandwritingInitiator().onInputConnectionCreated(view);
diff --git a/core/java/com/android/internal/app/procstats/IProcessStats.aidl b/core/java/com/android/internal/app/procstats/IProcessStats.aidl
index a2eca3a..84b2a35 100644
--- a/core/java/com/android/internal/app/procstats/IProcessStats.aidl
+++ b/core/java/com/android/internal/app/procstats/IProcessStats.aidl
@@ -21,7 +21,9 @@
import com.android.internal.app.procstats.ProcessStats;
interface IProcessStats {
+ @EnforcePermission("PACKAGE_USAGE_STATS")
byte[] getCurrentStats(out List<ParcelFileDescriptor> historic);
+ @EnforcePermission("PACKAGE_USAGE_STATS")
ParcelFileDescriptor getStatsOverTime(long minTime);
int getCurrentMemoryState();
@@ -43,6 +45,7 @@
* @param List of Files of individual commits in protobuf binary or one that is merged from them.
* @param ProcessStats object that will be used to return the full set of merged stats.
*/
+ @EnforcePermission("PACKAGE_USAGE_STATS")
long getCommittedStatsMerged(long highWaterMarkMs, int section, boolean doAggregate,
out List<ParcelFileDescriptor> committedStats, out ProcessStats mergedStats);
diff --git a/core/java/com/android/internal/inputmethod/ImeTracing.java b/core/java/com/android/internal/inputmethod/ImeTracing.java
index 8b21b7e..ee67131 100644
--- a/core/java/com/android/internal/inputmethod/ImeTracing.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracing.java
@@ -17,6 +17,7 @@
package com.android.internal.inputmethod;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.app.ActivityThread;
import android.content.Context;
import android.os.RemoteException;
@@ -93,6 +94,7 @@
/**
* Calling {@link IInputMethodManager#startImeTrace()}} to capture IME trace.
*/
+ @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING)
public final void startImeTrace() {
try {
mService.startImeTrace();
@@ -104,6 +106,7 @@
/**
* Calling {@link IInputMethodManager#stopImeTrace()} to stop IME trace.
*/
+ @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING)
public final void stopImeTrace() {
try {
mService.stopImeTrace();
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 1b77147..eb8b860 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -72,7 +72,8 @@
/* @android.view.WindowManager.LayoutParams.Flags */ int windowFlags,
in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection,
in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
- int unverifiedTargetSdkVersion, in ImeOnBackInvokedDispatcher imeDispatcher);
+ int unverifiedTargetSdkVersion, int userId,
+ in ImeOnBackInvokedDispatcher imeDispatcher);
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);
@@ -105,12 +106,22 @@
/** Remove the IME surface. Requires passing the currently focused window. */
oneway void removeImeSurfaceFromWindowAsync(in IBinder windowToken);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresNoPermission")
void startProtoDump(in byte[] protoDump, int source, String where);
+
boolean isImeTraceEnabled();
// Starts an ime trace.
+ @EnforcePermission("CONTROL_UI_TRACING")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ + "android.Manifest.permission.CONTROL_UI_TRACING)")
void startImeTrace();
+
// Stops an ime trace.
+ @EnforcePermission("CONTROL_UI_TRACING")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ + "android.Manifest.permission.CONTROL_UI_TRACING)")
void stopImeTrace();
/** Start Stylus handwriting session **/
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index d52c70b..ae1c9461 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -907,6 +907,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "-1237827119": {
+ "message": "Schedule remove starting %s startingWindow=%s animate=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-1228653755": {
"message": "Launch on display check: displayId=%d callingPid=%d callingUid=%d",
"level": "DEBUG",
@@ -1015,12 +1021,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowState.java"
},
- "-1128015008": {
- "message": "Schedule remove starting %s startingWindow=%s startingView=%s Callers=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STARTING_WINDOW",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-1117599386": {
"message": "Deferring rotation, display is not enabled.",
"level": "VERBOSE",
@@ -3139,6 +3139,12 @@
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/LockTaskController.java"
},
+ "956467125": {
+ "message": "Reparenting Activity to embedded TaskFragment, but the Activity is not collected",
+ "level": "WARN",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
+ },
"958338552": {
"message": "grantEmbeddedWindowFocus win=%s dropped focus so setting focus to null since no candidate was found",
"level": "VERBOSE",
@@ -3877,12 +3883,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1742235936": {
- "message": "Removing startingView=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STARTING_WINDOW",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"1746778201": {
"message": "Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
"level": "INFO",
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 596d061..f4f5e1e 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -50,6 +50,7 @@
import android.util.Log;
import android.util.LongSparseArray;
import android.util.LruCache;
+import android.util.Pair;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -1396,6 +1397,41 @@
}
}
+ /**
+ * Change default typefaces for testing purpose.
+ *
+ * Note: The existing TextView or Paint instance still holds the old Typeface.
+ *
+ * @param defaults array of [default, default_bold, default_italic, default_bolditalic].
+ * @param genericFamilies array of [sans-serif, serif, monospace]
+ * @return return the old defaults and genericFamilies
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public static Pair<List<Typeface>, List<Typeface>> changeDefaultFontForTest(
+ @NonNull List<Typeface> defaults,
+ @NonNull List<Typeface> genericFamilies
+ ) {
+ synchronized (SYSTEM_FONT_MAP_LOCK) {
+ List<Typeface> oldDefaults = Arrays.asList(sDefaults);
+ sDefaults = defaults.toArray(new Typeface[4]);
+ setDefault(defaults.get(0));
+
+ ArrayList<Typeface> oldGenerics = new ArrayList<>();
+ oldGenerics.add(sSystemFontMap.get("sans-serif"));
+ sSystemFontMap.put("sans-serif", genericFamilies.get(0));
+
+ oldGenerics.add(sSystemFontMap.get("serif"));
+ sSystemFontMap.put("serif", genericFamilies.get(1));
+
+ oldGenerics.add(sSystemFontMap.get("monospace"));
+ sSystemFontMap.put("monospace", genericFamilies.get(2));
+
+ return new Pair<>(oldDefaults, oldGenerics);
+ }
+ }
+
static {
// Preload Roboto-Regular.ttf in Zygote for improving app launch performance.
preloadFontFile("/system/fonts/Roboto-Regular.ttf");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index f85f9d6..5dd5149 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -411,8 +411,8 @@
@WMSingleton
@Provides
- static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper() {
- return new PipSurfaceTransactionHelper();
+ static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
+ return new PipSurfaceTransactionHelper(context);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index c0bc108..3ac08a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -39,6 +39,10 @@
private int mCornerRadius;
private int mShadowRadius;
+ public PipSurfaceTransactionHelper(Context context) {
+ onDensityOrFontScaleChanged(context);
+ }
+
/**
* Called when display size or font size of settings changed
*
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 913e4b7..1155ea1 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
@@ -445,7 +445,7 @@
// When exit to fullscreen with Shell transition enabled, we update the Task windowing
// mode directly so that it can also trigger display rotation and visibility update in
// the same transition if there will be any.
- wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+ wct.setWindowingMode(mToken, getOutPipWindowingMode());
// We can inherit the parent bounds as it is going to be fullscreen. The
// destinationBounds calculated above will be incorrect if this is with rotation.
wct.setBounds(mToken, null);
@@ -544,7 +544,7 @@
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setBounds(mToken, null);
- wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+ wct.setWindowingMode(mToken, getOutPipWindowingMode());
wct.reorder(mToken, false);
mPipTransitionController.startExitTransition(TRANSIT_REMOVE_PIP, wct,
null /* destinationBounds */);
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 53ec39d..7fb961f 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
@@ -352,41 +352,31 @@
public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options) {
+ if (fillInIntent == null) {
+ fillInIntent = new Intent();
+ }
+ // Flag this as a no-user-action launch to prevent sending user leaving event to the
+ // current top activity since it's going to be put into another side of the split. This
+ // prevents the current top activity from going into pip mode due to user leaving event.
+ fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
+
+ // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
+ // split.
+ if (isLaunchingAdjacently(intent.getIntent(), position)) {
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
+ }
+
if (!ENABLE_SHELL_TRANSITIONS) {
startIntentLegacy(intent, fillInIntent, position, options);
return;
}
- try {
- options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
- null /* wct */);
-
- if (fillInIntent == null) {
- fillInIntent = new Intent();
- }
- // Flag this as a no-user-action launch to prevent sending user leaving event to the
- // current top activity since it's going to be put into another side of the split. This
- // prevents the current top activity from going into pip mode due to user leaving event.
- fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
-
- // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
- // split.
- if (isLaunchingAdjacently(intent.getIntent(), position)) {
- fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
- }
-
- intent.send(mContext, 0, fillInIntent, null /* onFinished */, null /* handler */,
- null /* requiredPermission */, options);
- } catch (PendingIntent.CanceledException e) {
- Slog.e(TAG, "Failed to launch task", e);
- }
+ mStageCoordinator.startIntent(intent, fillInIntent, position, options);
}
- private void startIntentLegacy(PendingIntent intent, @Nullable Intent fillInIntent,
+ private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options) {
- boolean startSameActivityAdjacently = isLaunchingAdjacently(intent.getIntent(), position);
-
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
mStageCoordinator.prepareEvictChildTasks(position, evictWct);
@@ -397,8 +387,8 @@
IRemoteAnimationFinishedCallback finishedCallback,
SurfaceControl.Transaction t) {
if (apps == null || apps.length == 0) {
- if (startSameActivityAdjacently) {
- // Switch split position if dragging the same activity to another side.
+ // Switch the split position if launching as MULTIPLE_TASK failed.
+ if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
setSideStagePosition(SplitLayout.reversePosition(
mStageCoordinator.getSideStagePosition()));
}
@@ -408,8 +398,6 @@
return;
}
- mStageCoordinator.updateSurfaceBounds(null /* layout */, t,
- false /* applyResizingOffset */);
for (int i = 0; i < apps.length; ++i) {
if (apps[i].mode == MODE_OPENING) {
t.show(apps[i].leash);
@@ -432,18 +420,6 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
- // Flag this as a no-user-action launch to prevent sending user leaving event to the current
- // top activity since it's going to be put into another side of the split. This prevents the
- // current top activity from going into pip mode due to user leaving event.
- if (fillInIntent == null) {
- fillInIntent = new Intent();
- }
- fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
- if (startSameActivityAdjacently) {
- fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
- }
-
wct.sendPendingIntent(intent, fillInIntent, options);
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 056cd58..83bdf8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -62,13 +62,12 @@
private final Runnable mOnFinish;
DismissTransition mPendingDismiss = null;
- IBinder mPendingEnter = null;
- IBinder mPendingRecent = null;
+ TransitSession mPendingEnter = null;
+ TransitSession mPendingRecent = null;
private IBinder mAnimatingTransition = null;
OneShotRemoteHandler mPendingRemoteHandler = null;
private OneShotRemoteHandler mActiveRemoteHandler = null;
- private boolean mEnterTransitionMerged;
private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;
@@ -145,7 +144,7 @@
continue;
}
- if (transition == mPendingEnter && (mainRoot.equals(change.getContainer())
+ if (isPendingEnter(transition) && (mainRoot.equals(change.getContainer())
|| sideRoot.equals(change.getContainer()))) {
t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top);
t.setWindowCrop(leash, change.getEndAbsBounds().width(),
@@ -171,12 +170,40 @@
onFinish(null /* wct */, null /* wctCB */);
}
+ boolean isPendingTransition(IBinder transition) {
+ return isPendingEnter(transition)
+ || isPendingDismiss(transition)
+ || isPendingRecent(transition);
+ }
+
+ boolean isPendingEnter(IBinder transition) {
+ return mPendingEnter != null && mPendingEnter.mTransition == transition;
+ }
+
+ boolean isPendingRecent(IBinder transition) {
+ return mPendingRecent != null && mPendingRecent.mTransition == transition;
+ }
+
+ boolean isPendingDismiss(IBinder transition) {
+ return mPendingDismiss != null && mPendingDismiss.mTransition == transition;
+ }
+
/** Starts a transition to enter split with a remote transition animator. */
- IBinder startEnterTransition(@WindowManager.TransitionType int transitType,
- @NonNull WindowContainerTransaction wct, @Nullable RemoteTransition remoteTransition,
- @NonNull Transitions.TransitionHandler handler) {
+ IBinder startEnterTransition(
+ @WindowManager.TransitionType int transitType,
+ WindowContainerTransaction wct,
+ @Nullable RemoteTransition remoteTransition,
+ Transitions.TransitionHandler handler,
+ @Nullable TransitionCallback callback) {
final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
- mPendingEnter = transition;
+ setEnterTransition(transition, remoteTransition, callback);
+ return transition;
+ }
+
+ /** Sets a transition to enter split. */
+ void setEnterTransition(@NonNull IBinder transition,
+ @Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) {
+ mPendingEnter = new TransitSession(transition, callback);
if (remoteTransition != null) {
// Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
@@ -184,7 +211,9 @@
mTransitions.getMainExecutor(), remoteTransition);
mPendingRemoteHandler.setTransition(transition);
}
- return transition;
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ + " deduced Enter split screen");
}
/** Starts a transition to dismiss split. */
@@ -209,8 +238,8 @@
}
void setRecentTransition(@NonNull IBinder transition,
- @Nullable RemoteTransition remoteTransition) {
- mPendingRecent = transition;
+ @Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) {
+ mPendingRecent = new TransitSession(transition, callback);
if (remoteTransition != null) {
// Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
@@ -226,6 +255,18 @@
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
if (mergeTarget != mAnimatingTransition) return;
+
+ if (isPendingEnter(transition) && isPendingRecent(mergeTarget)) {
+ mPendingRecent.mCallback = new TransitionCallback() {
+ @Override
+ public void onTransitionFinished(WindowContainerTransaction finishWct,
+ SurfaceControl.Transaction finishT) {
+ // Since there's an entering transition merged, recent transition no longer
+ // need to handle entering split screen after the transition finished.
+ }
+ };
+ }
+
if (mActiveRemoteHandler != null) {
mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
} else {
@@ -247,38 +288,55 @@
}
void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
- if (aborted) return;
+ if (isPendingEnter(transition)) {
+ if (!aborted) {
+ // An enter transition got merged, appends the rest operations to finish entering
+ // split screen.
+ // TODO (b/238856352): Passed-in the proper finish transition to merge instead.
+ if (mFinishTransaction == null) {
+ mFinishTransaction = mTransactionPool.acquire();
+ }
+ mStageCoordinator.finishEnterSplitScreen(mFinishTransaction);
+ }
- // Once a pending enter transition got merged, make sure to append the reset of finishing
- // operations to the finish transition.
- if (transition == mPendingEnter) {
- mFinishTransaction = mTransactionPool.acquire();
- mStageCoordinator.finishEnterSplitScreen(mFinishTransaction);
+ mPendingEnter.mCallback.onTransitionConsumed(aborted);
mPendingEnter = null;
mPendingRemoteHandler = null;
- mEnterTransitionMerged = true;
+ } else if (isPendingDismiss(transition)) {
+ mPendingDismiss.mCallback.onTransitionConsumed(aborted);
+ mPendingDismiss = null;
+ } else if (isPendingRecent(transition)) {
+ mPendingRecent.mCallback.onTransitionConsumed(aborted);
+ mPendingRecent = null;
+ mPendingRemoteHandler = null;
}
}
void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) {
if (!mAnimations.isEmpty()) return;
- if (mAnimatingTransition == mPendingEnter) {
+
+ TransitionCallback callback = null;
+ if (isPendingEnter(mAnimatingTransition)) {
+ callback = mPendingEnter.mCallback;
mPendingEnter = null;
}
- if (mPendingDismiss != null && mPendingDismiss.mTransition == mAnimatingTransition) {
+ if (isPendingDismiss(mAnimatingTransition)) {
+ callback = mPendingDismiss.mCallback;
mPendingDismiss = null;
}
- if (mAnimatingTransition == mPendingRecent) {
- if (!mEnterTransitionMerged) {
- if (wct == null) wct = new WindowContainerTransaction();
- mStageCoordinator.onRecentTransitionFinished(wct, mFinishTransaction);
- }
+ if (isPendingRecent(mAnimatingTransition)) {
+ callback = mPendingRecent.mCallback;
mPendingRecent = null;
}
+
+ if (callback != null) {
+ if (wct == null) wct = new WindowContainerTransaction();
+ callback.onTransitionFinished(wct, mFinishTransaction);
+ }
+
mPendingRemoteHandler = null;
mActiveRemoteHandler = null;
mAnimatingTransition = null;
- mEnterTransitionMerged = false;
mOnFinish.run();
if (mFinishTransaction != null) {
@@ -382,17 +440,34 @@
|| info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
}
+ /** Clean-up callbacks for transition. */
+ interface TransitionCallback {
+ /** Calls when the transition got consumed. */
+ default void onTransitionConsumed(boolean aborted) {}
+
+ /** Calls when the transition finished. */
+ default void onTransitionFinished(WindowContainerTransaction finishWct,
+ SurfaceControl.Transaction finishT) {}
+ }
+
+ /** Session for a transition and its clean-up callback. */
+ static class TransitSession {
+ final IBinder mTransition;
+ TransitionCallback mCallback;
+
+ TransitSession(IBinder transition, @Nullable TransitionCallback callback) {
+ mTransition = transition;
+ mCallback = callback != null ? callback : new TransitionCallback() {};
+ }
+ }
+
/** Bundled information of dismiss transition. */
- static class DismissTransition {
- IBinder mTransition;
-
- int mReason;
-
- @SplitScreen.StageType
- int mDismissTop;
+ static class DismissTransition extends TransitSession {
+ final int mReason;
+ final @SplitScreen.StageType int mDismissTop;
DismissTransition(IBinder transition, int reason, int dismissTop) {
- this.mTransition = transition;
+ super(transition, null /* callback */);
this.mReason = reason;
this.mDismissTop = dismissTop;
}
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 2229e26..3c7db33 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
@@ -24,6 +24,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -214,6 +215,33 @@
}
};
+ private final SplitScreenTransitions.TransitionCallback mRecentTransitionCallback =
+ new SplitScreenTransitions.TransitionCallback() {
+ @Override
+ public void onTransitionFinished(WindowContainerTransaction finishWct,
+ SurfaceControl.Transaction finishT) {
+ // Check if the recent transition is finished by returning to the current split, so we
+ // can restore the divider bar.
+ for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
+ final WindowContainerTransaction.HierarchyOp op =
+ finishWct.getHierarchyOps().get(i);
+ final IBinder container = op.getContainer();
+ if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
+ && (mMainStage.containsContainer(container)
+ || mSideStage.containsContainer(container))) {
+ setDividerVisibility(true, finishT);
+ return;
+ }
+ }
+
+ // Dismiss the split screen if it's not returning to split.
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
+ setSplitsVisible(false);
+ setDividerVisibility(false, finishT);
+ logExit(EXIT_REASON_UNKNOWN);
+ }
+ };
+
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
ShellTaskOrganizer taskOrganizer, DisplayController displayController,
DisplayImeController displayImeController,
@@ -337,15 +365,23 @@
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
targetStage.evictAllChildren(evictWct);
targetStage.addTask(task, wct);
- if (!evictWct.isEmpty()) {
- wct.merge(evictWct, true /* transfer */);
- }
if (ENABLE_SHELL_TRANSITIONS) {
prepareEnterSplitScreen(wct);
- mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE,
- wct, null, this);
+ mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct,
+ null, this, new SplitScreenTransitions.TransitionCallback() {
+ @Override
+ public void onTransitionFinished(WindowContainerTransaction finishWct,
+ SurfaceControl.Transaction finishT) {
+ if (!evictWct.isEmpty()) {
+ finishWct.merge(evictWct, true);
+ }
+ }
+ });
} else {
+ if (!evictWct.isEmpty()) {
+ wct.merge(evictWct, true /* transfer */);
+ }
mTaskOrganizer.applyTransaction(wct);
}
return true;
@@ -365,6 +401,39 @@
return result;
}
+ /** Launches an activity into split. */
+ void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
+ @Nullable Bundle options) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ prepareEvictChildTasks(position, evictWct);
+
+ options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
+ wct.sendPendingIntent(intent, fillInIntent, options);
+ prepareEnterSplitScreen(wct, null /* taskInfo */, position);
+
+ mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, null, this,
+ new SplitScreenTransitions.TransitionCallback() {
+ @Override
+ public void onTransitionConsumed(boolean aborted) {
+ // Switch the split position if launching as MULTIPLE_TASK failed.
+ if (aborted
+ && (fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
+ setSideStagePositionAnimated(
+ SplitLayout.reversePosition(mSideStagePosition));
+ }
+ }
+
+ @Override
+ public void onTransitionFinished(WindowContainerTransaction finishWct,
+ SurfaceControl.Transaction finishT) {
+ if (!evictWct.isEmpty()) {
+ finishWct.merge(evictWct, true);
+ }
+ }
+ });
+ }
+
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
@Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
@@ -395,7 +464,7 @@
wct.startTask(sideTaskId, sideOptions);
mSplitTransitions.startEnterTransition(
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this);
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null);
}
/** Starts 2 tasks in one legacy transition. */
@@ -617,11 +686,13 @@
}
int getTaskId(@SplitPosition int splitPosition) {
- if (mSideStagePosition == splitPosition) {
- return mSideStage.getTopVisibleChildTaskId();
- } else {
- return mMainStage.getTopVisibleChildTaskId();
+ if (splitPosition == SPLIT_POSITION_UNDEFINED) {
+ return INVALID_TASK_ID;
}
+
+ return mSideStagePosition == splitPosition
+ ? mSideStage.getTopVisibleChildTaskId()
+ : mMainStage.getTopVisibleChildTaskId();
}
void setSideStagePositionAnimated(@SplitPosition int sideStagePosition) {
@@ -861,6 +932,7 @@
mSplitLayout.init();
setDividerVisibility(true, t);
updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+ t.show(mRootTaskLeash);
setSplitsVisible(true);
mShouldUpdateRecents = true;
updateRecentTasksSplitPair();
@@ -1211,7 +1283,7 @@
private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
final boolean hasChildren = stageListener.mHasChildren;
final boolean isSideStage = stageListener == mSideStageListener;
- if (!hasChildren && !mIsExiting) {
+ if (!hasChildren && !mIsExiting && mMainStage.isActive()) {
if (isSideStage && mMainStageListener.mVisible) {
// Exit to main stage if side stage no longer has children.
if (ENABLE_SHELL_TRANSITIONS) {
@@ -1231,7 +1303,7 @@
EXIT_REASON_APP_FINISHED);
}
}
- } else if (isSideStage && !mMainStage.isActive()) {
+ } else if (isSideStage && hasChildren && !mMainStage.isActive()) {
if (mFocusingTaskInfo != null && !isValidToEnterSplitScreen(mFocusingTaskInfo)) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSideStage.removeAllTasks(wct, true);
@@ -1543,14 +1615,14 @@
} else if (activityType == ACTIVITY_TYPE_HOME
|| activityType == ACTIVITY_TYPE_RECENTS) {
// Enter overview panel, so start recent transition.
- mSplitTransitions.setRecentTransition(transition,
- request.getRemoteTransition());
+ mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition(),
+ mRecentTransitionCallback);
} else if (mSplitTransitions.mPendingRecent == null) {
// If split-task is not controlled by recents animation
// and occluded by the other fullscreen task, dismiss both.
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
- mSplitTransitions.setDismissTransition(transition,
- STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN);
+ mSplitTransitions.setDismissTransition(
+ transition, STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN);
}
}
} else {
@@ -1558,7 +1630,8 @@
// One task is appearing into split, prepare to enter split screen.
out = new WindowContainerTransaction();
prepareEnterSplitScreen(out);
- mSplitTransitions.mPendingEnter = transition;
+ mSplitTransitions.setEnterTransition(
+ transition, request.getRemoteTransition(), null /* callback */);
}
}
return out;
@@ -1614,10 +1687,7 @@
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (transition != mSplitTransitions.mPendingEnter
- && transition != mSplitTransitions.mPendingRecent
- && (mSplitTransitions.mPendingDismiss == null
- || mSplitTransitions.mPendingDismiss.mTransition != transition)) {
+ if (!mSplitTransitions.isPendingTransition(transition)) {
// Not entering or exiting, so just do some house-keeping and validation.
// If we're not in split-mode, just abort so something else can handle it.
@@ -1664,12 +1734,11 @@
}
boolean shouldAnimate = true;
- if (mSplitTransitions.mPendingEnter == transition) {
+ if (mSplitTransitions.isPendingEnter(transition)) {
shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
- } else if (mSplitTransitions.mPendingRecent == transition) {
+ } else if (mSplitTransitions.isPendingRecent(transition)) {
shouldAnimate = startPendingRecentAnimation(transition, info, startTransaction);
- } else if (mSplitTransitions.mPendingDismiss != null
- && mSplitTransitions.mPendingDismiss.mTransition == transition) {
+ } else if (mSplitTransitions.isPendingDismiss(transition)) {
shouldAnimate = startPendingDismissAnimation(
mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
}
@@ -1837,28 +1906,6 @@
return true;
}
- void onRecentTransitionFinished(WindowContainerTransaction wct,
- SurfaceControl.Transaction finishT) {
- // Check if the recent transition is finished by returning to the current split so we can
- // restore the divider bar.
- for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
- final WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
- final IBinder container = op.getContainer();
- if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
- && (mMainStage.containsContainer(container)
- || mSideStage.containsContainer(container))) {
- setDividerVisibility(true, finishT);
- return;
- }
- }
-
- // Dismiss the split screen is it's not returning to split.
- prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
- setSplitsVisible(false);
- setDividerVisibility(false, finishT);
- logExit(EXIT_REASON_UNKNOWN);
- }
-
private void addDividerBarToTransition(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, boolean show) {
final SurfaceControl leash = mSplitLayout.getDividerLeash();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index dcd6277..0bec543 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -483,11 +483,11 @@
postStartTransactionCallbacks.add(t ->
startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
mTransactionPool, mMainExecutor, mAnimExecutor,
- null /* position */, cornerRadius, clipRect));
+ change.getEndRelOffset(), cornerRadius, clipRect));
} else {
startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
- mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
- cornerRadius, clipRect);
+ mTransactionPool, mMainExecutor, mAnimExecutor,
+ change.getEndRelOffset(), cornerRadius, clipRect);
}
if (info.getAnimationOptions() != null) {
@@ -934,7 +934,7 @@
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, mAnimExecutor, new Point(bounds.left, bounds.top),
+ mMainExecutor, mAnimExecutor, change.getEndRelOffset(),
cornerRadius, change.getEndAbsBounds());
}
@@ -959,7 +959,7 @@
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, mAnimExecutor, null /* position */,
+ mMainExecutor, mAnimExecutor, change.getEndRelOffset(),
cornerRadius, change.getEndAbsBounds());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index c19a33a..4855fbd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -222,10 +222,10 @@
float shadowRadius = outResult.mDensity * shadowRadiusDp;
int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor();
- mTmpColor[0] = Color.red(backgroundColorInt);
- mTmpColor[1] = Color.green(backgroundColorInt);
- mTmpColor[2] = Color.blue(backgroundColorInt);
- t.setCrop(mTaskBackgroundSurface, taskBounds)
+ mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f;
+ mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f;
+ mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
+ t.setWindowCrop(mTaskBackgroundSurface, taskBounds.width(), taskBounds.height())
.setShadowRadius(mTaskBackgroundSurface, shadowRadius)
.setColor(mTaskBackgroundSurface, mTmpColor);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java
index 4922872..f8b3fb3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java
@@ -34,13 +34,12 @@
* given {@link SurfaceControl} when calling {@link SurfaceControl.Builder#build()}.
*
* @param mockSurfaceControl the first {@link SurfaceControl} to return
- * @param mockSurfaceControls following {@link SurfaceControl} to return
* @return the mock of {@link SurfaceControl.Builder}
*/
public static SurfaceControl.Builder createMockSurfaceControlBuilder(
- SurfaceControl mockSurfaceControl, SurfaceControl... mockSurfaceControls) {
+ SurfaceControl mockSurfaceControl) {
final SurfaceControl.Builder mockBuilder = mock(SurfaceControl.Builder.class, RETURNS_SELF);
- doReturn(mockSurfaceControl, (Object[]) mockSurfaceControls)
+ doReturn(mockSurfaceControl)
.when(mockBuilder)
.build();
return mockBuilder;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 52d78ca..5880ffb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -37,6 +37,7 @@
import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.wm.shell.MockSurfaceControlHelper;
import com.android.wm.shell.ShellTestCase;
@@ -62,19 +63,18 @@
@Mock
private TaskInfo mTaskInfo;
-
@Mock
private PipAnimationController.PipAnimationCallback mPipAnimationCallback;
@Before
public void setUp() throws Exception {
- mPipAnimationController = new PipAnimationController(
- new PipSurfaceTransactionHelper());
+ MockitoAnnotations.initMocks(this);
+ mPipAnimationController = new PipAnimationController(new PipSurfaceTransactionHelper(
+ InstrumentationRegistry.getInstrumentation().getTargetContext()));
mLeash = new SurfaceControl.Builder()
.setContainerLayer()
.setName("FakeLeash")
.build();
- MockitoAnnotations.initMocks(this);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 304ca66..1d038f4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -182,7 +182,7 @@
IBinder transition = mSplitScreenTransitions.startEnterTransition(
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
- new RemoteTransition(testRemote), mStageCoordinator);
+ new RemoteTransition(testRemote), mStageCoordinator, null);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -422,7 +422,7 @@
TransitionInfo enterInfo = createEnterPairInfo();
IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
- new RemoteTransition(new TestRemoteTransition()), mStageCoordinator);
+ new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
mStageCoordinator.startAnimation(enterTransit, enterInfo,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 680034bd..d1b837e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -21,23 +21,32 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
+import android.util.DisplayMetrics;
import android.view.Display;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.WindowManager.LayoutParams;
import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
@@ -53,6 +62,8 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Supplier;
/**
@@ -66,6 +77,8 @@
public class WindowDecorationTests extends ShellTestCase {
private static final int CAPTION_HEIGHT_DP = 32;
private static final int SHADOW_RADIUS_DP = 5;
+ private static final Rect TASK_BOUNDS = new Rect(100, 300, 400, 400);
+ private static final Point TASK_POSITION_IN_PARENT = new Point(40, 60);
private final Rect mOutsetsDp = new Rect();
private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult =
@@ -84,12 +97,11 @@
@Mock
private WindowContainerTransaction mMockWindowContainerTransaction;
- private SurfaceControl.Builder mMockSurfaceControlBuilder;
+ private final List<SurfaceControl.Builder> mMockSurfaceControlBuilders = new ArrayList<>();
private SurfaceControl.Transaction mMockSurfaceControlTransaction;
@Before
public void setUp() {
- mMockSurfaceControlBuilder = createMockSurfaceControlBuilder(mock(SurfaceControl.class));
mMockSurfaceControlTransaction = createMockSurfaceControlTransaction();
doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
@@ -97,6 +109,128 @@
}
@Test
+ public void testLayoutResultCalculation_invisibleTask() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder decorContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(decorContainerSurface);
+ mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+ final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+ createMockSurfaceControlBuilder(taskBackgroundSurface);
+ mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder()
+ .setBackgroundColor(Color.YELLOW);
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setBounds(TASK_BOUNDS)
+ .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+ .setVisible(false)
+ .build();
+ taskInfo.isFocused = false;
+ // Density is 2. Outsets are (20, 40, 60, 80) px. Shadow radius is 10px. Caption height is
+ // 64px.
+ taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+ mOutsetsDp.set(10, 20, 30, 40);
+
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ windowDecor.relayout(taskInfo);
+
+ verify(decorContainerSurfaceBuilder, never()).build();
+ verify(taskBackgroundSurfaceBuilder, never()).build();
+ verify(mMockSurfaceControlViewHostFactory, never())
+ .create(any(), any(), any(), anyBoolean());
+
+ verify(mMockSurfaceControlTransaction).hide(taskSurface);
+
+ assertNull(mRelayoutResult.mRootView);
+ }
+
+ @Test
+ public void testLayoutResultCalculation_visibleFocusedTask() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder decorContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(decorContainerSurface);
+ mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+ final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+ createMockSurfaceControlBuilder(taskBackgroundSurface);
+ mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder()
+ .setBackgroundColor(Color.YELLOW);
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setBounds(TASK_BOUNDS)
+ .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+ .setVisible(true)
+ .build();
+ taskInfo.isFocused = true;
+ // Density is 2. Outsets are (20, 40, 60, 80) px. Shadow radius is 10px. Caption height is
+ // 64px.
+ taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+ mOutsetsDp.set(10, 20, 30, 40);
+
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ windowDecor.relayout(taskInfo);
+
+ verify(decorContainerSurfaceBuilder).setParent(taskSurface);
+ verify(decorContainerSurfaceBuilder).setContainerLayer();
+ verify(mMockSurfaceControlTransaction).setTrustedOverlay(decorContainerSurface, true);
+ verify(mMockSurfaceControlTransaction).setPosition(decorContainerSurface, -20, -40);
+ verify(mMockSurfaceControlTransaction).setWindowCrop(decorContainerSurface, 380, 220);
+
+ verify(taskBackgroundSurfaceBuilder).setParent(taskSurface);
+ verify(taskBackgroundSurfaceBuilder).setEffectLayer();
+ verify(mMockSurfaceControlTransaction).setWindowCrop(taskBackgroundSurface, 300, 100);
+ verify(mMockSurfaceControlTransaction)
+ .setColor(taskBackgroundSurface, new float[] {1.f, 1.f, 0.f});
+ verify(mMockSurfaceControlTransaction).setShadowRadius(taskBackgroundSurface, 10);
+
+ verify(mMockSurfaceControlViewHostFactory)
+ .create(any(), eq(defaultDisplay), any(), anyBoolean());
+ verify(mMockSurfaceControlViewHost)
+ .setView(same(mMockView),
+ argThat(lp -> lp.height == 64
+ && lp.width == 300
+ && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0));
+ if (ViewRootImpl.CAPTION_ON_SHELL) {
+ verify(mMockView).setTaskFocusState(true);
+ verify(mMockWindowContainerTransaction)
+ .addRectInsetsProvider(taskInfo.token,
+ new Rect(100, 300, 400, 364),
+ new int[] { InsetsState.ITYPE_CAPTION_BAR });
+ }
+
+ verify(mMockSurfaceControlTransaction)
+ .setPosition(taskSurface, TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y);
+ verify(mMockSurfaceControlTransaction)
+ .setCrop(taskSurface, new Rect(-20, -40, 360, 180));
+ verify(mMockSurfaceControlTransaction)
+ .show(taskSurface);
+
+ assertEquals(380, mRelayoutResult.mWidth);
+ assertEquals(220, mRelayoutResult.mHeight);
+ assertEquals(2, mRelayoutResult.mDensity, 0.f);
+ }
+
+ @Test
public void testNotCrashWhenDisplayAppearsAfterTask() {
doReturn(mock(Display.class)).when(mMockDisplayController)
.getDisplay(Display.DEFAULT_DISPLAY);
@@ -145,10 +279,24 @@
private TestWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
- taskInfo, testSurface, () -> mMockSurfaceControlBuilder,
+ taskInfo, testSurface, new MockSurfaceControlBuilderSupplier(),
mMockSurfaceControlViewHostFactory);
}
+ private class MockSurfaceControlBuilderSupplier implements Supplier<SurfaceControl.Builder> {
+ private int mNumOfCalls = 0;
+
+ @Override
+ public SurfaceControl.Builder get() {
+ final SurfaceControl.Builder builder =
+ mNumOfCalls < mMockSurfaceControlBuilders.size()
+ ? mMockSurfaceControlBuilders.get(mNumOfCalls)
+ : createMockSurfaceControlBuilder(mock(SurfaceControl.class));
+ ++mNumOfCalls;
+ return builder;
+ }
+ }
+
private static class TestView extends View implements TaskFocusStateConsumer {
private TestView(Context context) {
super(context);
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 30d6d00..19efc5f 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -21,6 +21,7 @@
#include <minikin/FontFamily.h>
#include <minikin/FontFileParser.h>
#include <minikin/LocaleList.h>
+#include <minikin/MinikinFontFactory.h>
#include <minikin/SystemFonts.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
@@ -206,9 +207,18 @@
return entry;
}
-static std::shared_ptr<minikin::MinikinFont> loadMinikinFontSkia(minikin::BufferReader);
+class MinikinFontSkiaFactory : minikin::MinikinFontFactory {
+private:
+ MinikinFontSkiaFactory() : MinikinFontFactory() { MinikinFontFactory::setInstance(this); }
-static minikin::Font::TypefaceLoader* readMinikinFontSkia(minikin::BufferReader* reader) {
+public:
+ static void init() { static MinikinFontSkiaFactory factory; }
+ void skip(minikin::BufferReader* reader) const override;
+ std::shared_ptr<minikin::MinikinFont> create(minikin::BufferReader reader) const override;
+ void write(minikin::BufferWriter* writer, const minikin::MinikinFont* typeface) const override;
+};
+
+void MinikinFontSkiaFactory::skip(minikin::BufferReader* reader) const {
// Advance reader's position.
reader->skipString(); // fontPath
reader->skip<int>(); // fontIndex
@@ -218,10 +228,10 @@
reader->skip<uint32_t>(); // expectedFontRevision
reader->skipString(); // expectedPostScriptName
}
- return &loadMinikinFontSkia;
}
-static std::shared_ptr<minikin::MinikinFont> loadMinikinFontSkia(minikin::BufferReader reader) {
+std::shared_ptr<minikin::MinikinFont> MinikinFontSkiaFactory::create(
+ minikin::BufferReader reader) const {
std::string_view fontPath = reader.readString();
std::string path(fontPath.data(), fontPath.size());
ATRACE_FORMAT("Loading font %s", path.c_str());
@@ -270,8 +280,8 @@
return minikinFont;
}
-static void writeMinikinFontSkia(minikin::BufferWriter* writer,
- const minikin::MinikinFont* typeface) {
+void MinikinFontSkiaFactory::write(minikin::BufferWriter* writer,
+ const minikin::MinikinFont* typeface) const {
// When you change the format of font metadata, please update code to parse
// typefaceMetadataReader() in
// frameworks/base/libs/hwui/jni/fonts/Font.cpp too.
@@ -296,6 +306,7 @@
}
static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongArray faceHandles) {
+ MinikinFontSkiaFactory::init();
ScopedLongArrayRO faces(env, faceHandles);
std::vector<Typeface*> typefaces;
typefaces.reserve(faces.size());
@@ -312,7 +323,7 @@
fontCollections.push_back(typeface->fFontCollection);
}
}
- minikin::FontCollection::writeVector<writeMinikinFontSkia>(&writer, fontCollections);
+ minikin::FontCollection::writeVector(&writer, fontCollections);
writer.write<uint32_t>(typefaces.size());
for (Typeface* typeface : typefaces) {
writer.write<uint32_t>(fcToIndex.find(typeface->fFontCollection)->second);
@@ -324,11 +335,12 @@
}
static jlongArray Typeface_readTypefaces(JNIEnv *env, jobject, jobject buffer) {
+ MinikinFontSkiaFactory::init();
void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
if (addr == nullptr) return nullptr;
minikin::BufferReader reader(addr);
std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections =
- minikin::FontCollection::readVector<readMinikinFontSkia>(&reader);
+ minikin::FontCollection::readVector(&reader);
uint32_t typefaceCount = reader.read<uint32_t>();
std::vector<jlong> faceHandles;
faceHandles.reserve(typefaceCount);
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 57f1056..a7a21e7 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -48,6 +48,7 @@
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@@ -124,6 +125,7 @@
private final Map<String, RoutingController> mNonSystemRoutingControllers = new ArrayMap<>();
private final AtomicInteger mNextRequestId = new AtomicInteger(1);
+ private final AtomicBoolean mIsScanning = new AtomicBoolean(/* initialValue= */ false);
final Handler mHandler;
@@ -255,7 +257,9 @@
@RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
public void startScan() {
if (isSystemRouter()) {
- sManager.startScan();
+ if (!mIsScanning.getAndSet(true)) {
+ sManager.registerScanRequest();
+ }
}
}
@@ -281,7 +285,9 @@
@RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
public void stopScan() {
if (isSystemRouter()) {
- sManager.stopScan();
+ if (mIsScanning.getAndSet(false)) {
+ sManager.unregisterScanRequest();
+ }
}
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index c84f5b0..44c0b54 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -82,6 +82,7 @@
@GuardedBy("sLock")
private Client mClient;
private final IMediaRouterService mMediaRouterService;
+ private final AtomicInteger mScanRequestCount = new AtomicInteger(/* initialValue= */ 0);
final Handler mHandler;
final CopyOnWriteArrayList<CallbackRecord> mCallbackRecords = new CopyOnWriteArrayList<>();
@@ -155,22 +156,18 @@
}
/**
- * Starts scanning remote routes.
- * <p>
- * Route discovery can happen even when the {@link #startScan()} is not called.
- * This is because the scanning could be started before by other apps.
- * Therefore, calling this method after calling {@link #stopScan()} does not necessarily mean
- * that the routes found before are removed and added again.
- * <p>
- * Use {@link Callback} to get the route related events.
- * <p>
- * @see #stopScan()
+ * Registers a request to scan for remote routes.
+ *
+ * <p>Increases the count of active scanning requests. When the count transitions from zero to
+ * one, sends a request to the system server to start scanning.
+ *
+ * <p>Clients must {@link #unregisterScanRequest() unregister their scan requests} when scanning
+ * is no longer needed, to avoid unnecessary resource usage.
*/
- public void startScan() {
- Client client = getOrCreateClient();
- if (client != null) {
+ public void registerScanRequest() {
+ if (mScanRequestCount.getAndIncrement() == 0) {
try {
- mMediaRouterService.startScan(client);
+ mMediaRouterService.startScan(getOrCreateClient());
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -178,23 +175,26 @@
}
/**
- * Stops scanning remote routes to reduce resource consumption.
- * <p>
- * Route discovery can be continued even after this method is called.
- * This is because the scanning is only turned off when all the apps stop scanning.
- * Therefore, calling this method does not necessarily mean the routes are removed.
- * Also, for the same reason it does not mean that {@link Callback#onRoutesAdded(List)}
- * is not called afterwards.
- * <p>
- * Use {@link Callback} to get the route related events.
+ * Unregisters a scan request made by {@link #registerScanRequest()}.
*
- * @see #startScan()
+ * <p>Decreases the count of active scanning requests. When the count transitions from one to
+ * zero, sends a request to the system server to stop scanning.
+ *
+ * @throws IllegalStateException If called while there are no active scan requests.
*/
- public void stopScan() {
- Client client = getOrCreateClient();
- if (client != null) {
+ public void unregisterScanRequest() {
+ if (mScanRequestCount.updateAndGet(
+ count -> {
+ if (count == 0) {
+ throw new IllegalStateException(
+ "No active scan requests to unregister.");
+ } else {
+ return --count;
+ }
+ })
+ == 0) {
try {
- mMediaRouterService.stopScan(client);
+ mMediaRouterService.stopScan(getOrCreateClient());
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -423,15 +423,11 @@
*/
@NonNull
public List<RoutingSessionInfo> getRemoteSessions() {
- Client client = getOrCreateClient();
- if (client != null) {
- try {
- return mMediaRouterService.getRemoteSessions(client);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ try {
+ return mMediaRouterService.getRemoteSessions(getOrCreateClient());
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
}
- return Collections.emptyList();
}
/**
@@ -514,14 +510,12 @@
return;
}
- Client client = getOrCreateClient();
- if (client != null) {
- try {
- int requestId = mNextRequestId.getAndIncrement();
- mMediaRouterService.setRouteVolumeWithManager(client, requestId, route, volume);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ try {
+ int requestId = mNextRequestId.getAndIncrement();
+ mMediaRouterService.setRouteVolumeWithManager(
+ getOrCreateClient(), requestId, route, volume);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
}
}
@@ -543,15 +537,12 @@
return;
}
- Client client = getOrCreateClient();
- if (client != null) {
- try {
- int requestId = mNextRequestId.getAndIncrement();
- mMediaRouterService.setSessionVolumeWithManager(
- client, requestId, sessionInfo.getId(), volume);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ try {
+ int requestId = mNextRequestId.getAndIncrement();
+ mMediaRouterService.setSessionVolumeWithManager(
+ getOrCreateClient(), requestId, sessionInfo.getId(), volume);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
}
}
@@ -808,15 +799,12 @@
return;
}
- Client client = getOrCreateClient();
- if (client != null) {
- try {
- int requestId = mNextRequestId.getAndIncrement();
- mMediaRouterService.selectRouteWithManager(
- client, requestId, sessionInfo.getId(), route);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ try {
+ int requestId = mNextRequestId.getAndIncrement();
+ mMediaRouterService.selectRouteWithManager(
+ getOrCreateClient(), requestId, sessionInfo.getId(), route);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
}
}
@@ -850,15 +838,12 @@
return;
}
- Client client = getOrCreateClient();
- if (client != null) {
- try {
- int requestId = mNextRequestId.getAndIncrement();
- mMediaRouterService.deselectRouteWithManager(
- client, requestId, sessionInfo.getId(), route);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ try {
+ int requestId = mNextRequestId.getAndIncrement();
+ mMediaRouterService.deselectRouteWithManager(
+ getOrCreateClient(), requestId, sessionInfo.getId(), route);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
}
}
@@ -875,15 +860,12 @@
public void releaseSession(@NonNull RoutingSessionInfo sessionInfo) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
- Client client = getOrCreateClient();
- if (client != null) {
- try {
- int requestId = mNextRequestId.getAndIncrement();
- mMediaRouterService.releaseSessionWithManager(
- client, requestId, sessionInfo.getId());
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ try {
+ int requestId = mNextRequestId.getAndIncrement();
+ mMediaRouterService.releaseSessionWithManager(
+ getOrCreateClient(), requestId, sessionInfo.getId());
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
}
}
@@ -896,14 +878,11 @@
@NonNull MediaRoute2Info route) {
int requestId = createTransferRequest(session, route);
- Client client = getOrCreateClient();
- if (client != null) {
- try {
- mMediaRouterService.transferToRouteWithManager(
- client, requestId, session.getId(), route);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ try {
+ mMediaRouterService.transferToRouteWithManager(
+ getOrCreateClient(), requestId, session.getId(), route);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
}
}
@@ -916,14 +895,11 @@
int requestId = createTransferRequest(oldSession, route);
- Client client = getOrCreateClient();
- if (client != null) {
- try {
- mMediaRouterService.requestCreateSessionWithManager(
- client, requestId, oldSession, route);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ try {
+ mMediaRouterService.requestCreateSessionWithManager(
+ getOrCreateClient(), requestId, oldSession, route);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
}
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index b4aad9d..4086dec 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -39,6 +39,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import android.Manifest;
@@ -121,7 +122,7 @@
MediaRouter2ManagerTestActivity.startActivity(mContext);
mManager = MediaRouter2Manager.getInstance(mContext);
- mManager.startScan();
+ mManager.registerScanRequest();
mRouter2 = MediaRouter2.getInstance(mContext);
// If we need to support thread pool executors, change this to thread pool executor.
@@ -152,7 +153,7 @@
@After
public void tearDown() {
- mManager.stopScan();
+ mManager.unregisterScanRequest();
// order matters (callbacks should be cleared at the last)
releaseAllSessions();
@@ -818,6 +819,13 @@
assertFalse(failureLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
+ @Test
+ public void unregisterScanRequest_enforcesANonNegativeCount() {
+ mManager.unregisterScanRequest(); // One request was made in the test setup.
+ assertThrows(IllegalStateException.class, () -> mManager.unregisterScanRequest());
+ mManager.registerScanRequest(); // So that the cleanup doesn't fail.
+ }
+
/**
* Tests if getSelectableRoutes and getDeselectableRoutes filter routes based on
* selected routes
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 58944f6..4714ff9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -576,6 +576,15 @@
return;
}
+ // The profiles list's sequence will affect the bluetooth icon at
+ // BluetoothUtils.getBtClassDrawableWithDescription(Context,CachedBluetoothDevice).
+
+ // Moving the LE audio profile to be the first priority if the device supports LE audio.
+ if (ArrayUtils.contains(uuids, BluetoothUuid.LE_AUDIO) && mLeAudioProfile != null) {
+ profiles.add(mLeAudioProfile);
+ removedProfiles.remove(mLeAudioProfile);
+ }
+
if (mHeadsetProfile != null) {
if ((ArrayUtils.contains(localUuids, BluetoothUuid.HSP_AG)
&& ArrayUtils.contains(uuids, BluetoothUuid.HSP))
@@ -660,11 +669,6 @@
removedProfiles.remove(mHearingAidProfile);
}
- if (ArrayUtils.contains(uuids, BluetoothUuid.LE_AUDIO) && mLeAudioProfile != null) {
- profiles.add(mLeAudioProfile);
- removedProfiles.remove(mLeAudioProfile);
- }
-
if (mSapProfile != null && ArrayUtils.contains(uuids, BluetoothUuid.SAP)) {
profiles.add(mSapProfile);
removedProfiles.remove(mSapProfile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 192872f..d9262cc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -92,14 +92,14 @@
public void startScan() {
mMediaDevices.clear();
mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
- mRouterManager.startScan();
+ mRouterManager.registerScanRequest();
refreshDevices();
}
@Override
public void stopScan() {
mRouterManager.unregisterCallback(mMediaRouterCallback);
- mRouterManager.stopScan();
+ mRouterManager.unregisterScanRequest();
}
/**
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt
index 4d5014c..b8639e6 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt
@@ -37,7 +37,7 @@
* Important: Use M3 colors from MaterialTheme.colorScheme whenever possible instead. In the future,
* most of the colors in this class will be removed in favor of their M3 counterpart.
*/
-class AndroidColorScheme internal constructor(private val context: Context) {
+class AndroidColorScheme internal constructor(context: Context) {
val colorPrimary = getColor(context, R.attr.colorPrimary)
val colorPrimaryDark = getColor(context, R.attr.colorPrimaryDark)
val colorAccent = getColor(context, R.attr.colorAccent)
diff --git a/packages/SystemUI/compose/gallery/Android.bp b/packages/SystemUI/compose/gallery/Android.bp
index bddc1e6..40504dc 100644
--- a/packages/SystemUI/compose/gallery/Android.bp
+++ b/packages/SystemUI/compose/gallery/Android.bp
@@ -34,6 +34,7 @@
],
static_libs: [
+ "SystemUI-core",
"SystemUIComposeCore",
"SystemUIComposeFeatures",
diff --git a/packages/SystemUI/compose/gallery/AndroidManifest.xml b/packages/SystemUI/compose/gallery/AndroidManifest.xml
index 4fcce0b..2f30651 100644
--- a/packages/SystemUI/compose/gallery/AndroidManifest.xml
+++ b/packages/SystemUI/compose/gallery/AndroidManifest.xml
@@ -16,7 +16,40 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
package="com.android.systemui.compose.gallery">
<!-- To emulate a display size and density. -->
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+
+ <application
+ android:name="android.app.Application"
+ android:appComponentFactory="androidx.core.app.AppComponentFactory"
+ tools:replace="android:name,android:appComponentFactory">
+ <!-- Disable providers from SystemUI -->
+ <provider android:name="com.android.systemui.keyguard.KeyguardSliceProvider"
+ android:authorities="com.android.systemui.test.keyguard.disabled"
+ android:enabled="false"
+ tools:replace="android:authorities"
+ tools:node="remove" />
+ <provider android:name="com.google.android.systemui.keyguard.KeyguardSliceProviderGoogle"
+ android:authorities="com.android.systemui.test.keyguard.disabled"
+ android:enabled="false"
+ tools:replace="android:authorities"
+ tools:node="remove" />
+ <provider android:name="com.android.keyguard.clock.ClockOptionsProvider"
+ android:authorities="com.android.systemui.test.keyguard.clock.disabled"
+ android:enabled="false"
+ tools:replace="android:authorities"
+ tools:node="remove" />
+ <provider android:name="com.android.systemui.people.PeopleProvider"
+ android:authorities="com.android.systemui.test.people.disabled"
+ android:enabled="false"
+ tools:replace="android:authorities"
+ tools:node="remove" />
+ <provider android:name="androidx.core.content.FileProvider"
+ android:authorities="com.android.systemui.test.fileprovider.disabled"
+ android:enabled="false"
+ tools:replace="android:authorities"
+ tools:node="remove"/>
+ </application>
</manifest>
diff --git a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml b/packages/SystemUI/compose/gallery/app/AndroidManifest.xml
index e7d496c..1f3fd8c 100644
--- a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml
+++ b/packages/SystemUI/compose/gallery/app/AndroidManifest.xml
@@ -16,6 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
package="com.android.systemui.compose.gallery.app">
<application
android:allowBackup="true"
@@ -23,7 +24,8 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
- android:theme="@style/Theme.SystemUIGallery">
+ android:theme="@style/Theme.SystemUI.Gallery"
+ tools:replace="android:icon,android:theme,android:label">
<activity
android:name="com.android.systemui.compose.gallery.GalleryActivity"
android:exported="true"
diff --git a/packages/SystemUI/compose/gallery/res/values-night/themes.xml b/packages/SystemUI/compose/gallery/res/values-night/themes.xml
deleted file mode 100644
index 58d20b8..0000000
--- a/packages/SystemUI/compose/gallery/res/values-night/themes.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:tools="http://schemas.android.com/tools">
- <style name="Theme.SystemUIGallery" parent="Theme.AppCompat.DayNight.NoActionBar">
- <item name="android:statusBarColor" tools:targetApi="l">
- @android:color/transparent
- </item>
- <item name="android:navigationBarColor" tools:targetApi="l">
- @android:color/transparent
- </item>
- </style>
-</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/compose/gallery/res/values/themes.xml b/packages/SystemUI/compose/gallery/res/values/themes.xml
index 6e5e9983..45fa1f5d 100644
--- a/packages/SystemUI/compose/gallery/res/values/themes.xml
+++ b/packages/SystemUI/compose/gallery/res/values/themes.xml
@@ -15,7 +15,10 @@
limitations under the License.
-->
<resources xmlns:tools="http://schemas.android.com/tools">
- <style name="Theme.SystemUIGallery" parent="Theme.AppCompat.DayNight.NoActionBar">
+ <style name="Theme.SystemUI.Gallery">
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowNoTitle">true</item>
+
<item name="android:statusBarColor" tools:targetApi="l">
@android:color/transparent
</item>
@@ -24,4 +27,4 @@
</item>
<item name="android:windowLightStatusBar">true</item>
</style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt
index 06bdad8..990d060 100644
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt
@@ -9,11 +9,12 @@
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.BrightnessHigh
-import androidx.compose.material.icons.filled.BrightnessLow
+import androidx.compose.material.icons.filled.DarkMode
import androidx.compose.material.icons.filled.FormatSize
import androidx.compose.material.icons.filled.FormatTextdirectionLToR
import androidx.compose.material.icons.filled.FormatTextdirectionRToL
+import androidx.compose.material.icons.filled.InvertColors
+import androidx.compose.material.icons.filled.LightMode
import androidx.compose.material.icons.filled.Smartphone
import androidx.compose.material.icons.filled.Tablet
import androidx.compose.material3.Button
@@ -44,12 +45,13 @@
/** A configuration panel that allows to toggle the theme, font scale and layout direction. */
@Composable
fun ConfigurationControls(
- isDarkTheme: Boolean,
+ theme: Theme,
fontScale: FontScale,
layoutDirection: LayoutDirection,
onChangeTheme: () -> Unit,
onChangeLayoutDirection: () -> Unit,
onChangeFontScale: () -> Unit,
+ modifier: Modifier = Modifier,
) {
// The display we are emulating, if any.
var emulatedDisplayName by rememberSaveable { mutableStateOf<String?>(null) }
@@ -84,18 +86,26 @@
// TODO(b/231131244): Fork FlowRow from Accompanist and use that instead to make sure that users
// don't miss any available configuration.
- LazyRow {
+ LazyRow(modifier) {
// Dark/light theme.
item {
TextButton(onChangeTheme) {
val text: String
val icon: ImageVector
- if (isDarkTheme) {
- icon = Icons.Default.BrightnessHigh
- text = "Dark"
- } else {
- icon = Icons.Default.BrightnessLow
- text = "Light"
+
+ when (theme) {
+ Theme.System -> {
+ icon = Icons.Default.InvertColors
+ text = "System"
+ }
+ Theme.Dark -> {
+ icon = Icons.Default.DarkMode
+ text = "Dark"
+ }
+ Theme.Light -> {
+ icon = Icons.Default.LightMode
+ text = "Light"
+ }
}
Icon(icon, null)
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt
index d18b454..bb2d2fe 100644
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt
@@ -16,6 +16,8 @@
package com.android.systemui.compose.gallery
+import android.app.UiModeManager
+import android.content.Context
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
@@ -23,7 +25,7 @@
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
import androidx.core.view.WindowCompat
@@ -33,22 +35,46 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
+ val uiModeManager = getSystemService(Context.UI_MODE_SERVICE) as UiModeManager
setContent {
- val isSystemInDarkTheme = isSystemInDarkTheme()
- var isDarkTheme by remember { mutableStateOf(isSystemInDarkTheme) }
- val onChangeTheme = { isDarkTheme = !isDarkTheme }
+ var theme by rememberSaveable { mutableStateOf(Theme.System) }
+ val onChangeTheme = {
+ // Change to the next theme for a toggle behavior.
+ theme =
+ when (theme) {
+ Theme.System -> Theme.Dark
+ Theme.Dark -> Theme.Light
+ Theme.Light -> Theme.System
+ }
+ }
+ val isSystemInDarkTheme = isSystemInDarkTheme()
+ val isDark = theme == Theme.Dark || (theme == Theme.System && isSystemInDarkTheme)
+ val useDarkIcons = !isDark
val systemUiController = rememberSystemUiController()
- val useDarkIcons = !isDarkTheme
SideEffect {
systemUiController.setSystemBarsColor(
color = Color.Transparent,
darkIcons = useDarkIcons,
)
+
+ uiModeManager.setApplicationNightMode(
+ when (theme) {
+ Theme.System -> UiModeManager.MODE_NIGHT_AUTO
+ Theme.Dark -> UiModeManager.MODE_NIGHT_YES
+ Theme.Light -> UiModeManager.MODE_NIGHT_NO
+ }
+ )
}
- GalleryApp(isDarkTheme, onChangeTheme)
+ GalleryApp(theme, onChangeTheme)
}
}
}
+
+enum class Theme {
+ System,
+ Dark,
+ Light,
+}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
index 0ac3a63..c341867 100644
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
@@ -23,36 +23,38 @@
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.navigation.compose.NavHost
-import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.android.systemui.compose.theme.SystemUITheme
-enum class Screen {
- Home,
- Typography,
- MaterialColors,
- AndroidColors,
- ExampleFeature,
+/** The gallery app screens. */
+object GalleryAppScreens {
+ val Typography = ChildScreen("typography") { TypographyScreen() }
+ val MaterialColors = ChildScreen("material_colors") { MaterialColorsScreen() }
+ val AndroidColors = ChildScreen("android_colors") { AndroidColorsScreen() }
+ val ExampleFeature = ChildScreen("example_feature") { ExampleFeatureScreen() }
+
+ val Home =
+ ParentScreen(
+ "home",
+ mapOf(
+ "Typography" to Typography,
+ "Material colors" to MaterialColors,
+ "Android colors" to AndroidColors,
+ "Example feature" to ExampleFeature,
+ )
+ )
}
-/** The main content of the app, that shows the [HomeScreen] by default. */
+/** The main content of the app, that shows [GalleryAppScreens.Home] by default. */
@Composable
private fun MainContent() {
Box(Modifier.fillMaxSize()) {
val navController = rememberNavController()
NavHost(
navController = navController,
- startDestination = Screen.Home.name,
+ startDestination = GalleryAppScreens.Home.identifier,
) {
- composable(Screen.Home.name) {
- HomeScreen(
- onScreenSelected = { navController.navigate(it.name) },
- )
- }
- composable(Screen.Typography.name) { TypographyScreen() }
- composable(Screen.MaterialColors.name) { MaterialColorsScreen() }
- composable(Screen.AndroidColors.name) { AndroidColorsScreen() }
- composable(Screen.ExampleFeature.name) { ExampleFeatureScreen() }
+ screen(GalleryAppScreens.Home, navController)
}
}
}
@@ -63,7 +65,7 @@
*/
@Composable
fun GalleryApp(
- isDarkTheme: Boolean,
+ theme: Theme,
onChangeTheme: () -> Unit,
) {
val systemFontScale = LocalDensity.current.fontScale
@@ -98,14 +100,14 @@
LocalDensity provides density,
LocalLayoutDirection provides layoutDirection,
) {
- SystemUITheme(isDarkTheme) {
+ SystemUITheme {
Surface(
Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background,
) {
Column(Modifier.fillMaxSize().systemBarsPadding().padding(16.dp)) {
ConfigurationControls(
- isDarkTheme,
+ theme,
fontScale,
layoutDirection,
onChangeTheme,
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/HomeScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/HomeScreen.kt
deleted file mode 100644
index b1869da..0000000
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/HomeScreen.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.compose.gallery
-
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-
-/** The home screen shown when starting the app. */
-@Composable
-fun HomeScreen(onScreenSelected: (Screen) -> Unit) {
- LazyColumn(
- verticalArrangement = Arrangement.spacedBy(8.dp),
- ) {
- Screen.values()
- .filter { it != Screen.Home }
- .forEach { screen ->
- item {
- Surface(
- Modifier.fillMaxWidth(),
- color = MaterialTheme.colorScheme.secondaryContainer,
- shape = CircleShape,
- ) {
- Column(
- Modifier.clickable { onScreenSelected(screen) }.padding(16.dp),
- horizontalAlignment = Alignment.CenterHorizontally,
- ) {
- Text(screen.name)
- }
- }
- }
- }
- }
-}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt
new file mode 100644
index 0000000..467dac04
--- /dev/null
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.compose.gallery
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavController
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.navigation
+
+/**
+ * A screen in an app. It is either an [ParentScreen] which lists its child screens to navigate to
+ * them or a [ChildScreen] which shows some content.
+ */
+sealed class Screen(val identifier: String)
+
+class ParentScreen(
+ identifier: String,
+ val children: Map<String, Screen>,
+) : Screen(identifier)
+
+class ChildScreen(
+ identifier: String,
+ val content: @Composable (NavController) -> Unit,
+) : Screen(identifier)
+
+/** Create the navigation graph for [screen]. */
+fun NavGraphBuilder.screen(screen: Screen, navController: NavController) {
+ when (screen) {
+ is ChildScreen -> composable(screen.identifier) { screen.content(navController) }
+ is ParentScreen -> {
+ val menuRoute = "${screen.identifier}_menu"
+ navigation(startDestination = menuRoute, route = screen.identifier) {
+ // The menu to navigate to one of the children screens.
+ composable(menuRoute) { ScreenMenu(screen, navController) }
+
+ // The content of the child screens.
+ screen.children.forEach { (_, child) -> screen(child, navController) }
+ }
+ }
+ }
+}
+
+@Composable
+private fun ScreenMenu(
+ screen: ParentScreen,
+ navController: NavController,
+) {
+ LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) {
+ screen.children.forEach { (name, child) ->
+ item {
+ Surface(
+ Modifier.fillMaxWidth(),
+ color = MaterialTheme.colorScheme.secondaryContainer,
+ shape = CircleShape,
+ ) {
+ Column(
+ Modifier.clickable { navController.navigate(child.identifier) }
+ .padding(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Text(name)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2624412..ddbe6d6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -890,7 +890,7 @@
<!-- The maximum offset for the under-display fingerprint sensor (UDFPS) icon in either
direction that elements are moved to prevent burn-in on AOD-->
<dimen name="udfps_burn_in_offset_x">7px</dimen>
- <dimen name="udfps_burn_in_offset_y">28px</dimen>
+ <dimen name="udfps_burn_in_offset_y">20px</dimen>
<!-- The absolute side margins of quick settings -->
<dimen name="quick_settings_bottom_margin_media">8dp</dimen>
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 e3f5687..4222744 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
@@ -16,6 +16,7 @@
package com.android.systemui.shared.recents.model;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
@@ -245,12 +246,16 @@
*/
public static Task from(TaskKey taskKey, TaskInfo taskInfo, boolean isLocked) {
ActivityManager.TaskDescription td = taskInfo.taskDescription;
+ // Also consider undefined activity type to include tasks in overview right after rebooting
+ // the device.
+ final boolean isDockable = taskInfo.supportsMultiWindow
+ && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode())
+ && (taskInfo.getActivityType() == ACTIVITY_TYPE_UNDEFINED
+ || ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType()));
return new Task(taskKey,
td != null ? td.getPrimaryColor() : 0,
- td != null ? td.getBackgroundColor() : 0, taskInfo.supportsMultiWindow
- && ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
- && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode()),
- isLocked, td, taskInfo.topActivity);
+ td != null ? td.getBackgroundColor() : 0, isDockable , isLocked, td,
+ taskInfo.topActivity);
}
public Task(TaskKey key) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
index 8c8f54f..ad073c0 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
@@ -49,8 +49,8 @@
) : UserFileManager, CoreStartable(context) {
companion object {
private const val FILES = "files"
- private const val SHARED_PREFS = "shared_prefs"
- internal const val ID = "UserFileManager"
+ @VisibleForTesting internal const val SHARED_PREFS = "shared_prefs"
+ @VisibleForTesting internal const val ID = "UserFileManager"
}
private val broadcastReceiver = object : BroadcastReceiver() {
@@ -85,13 +85,15 @@
fileName
)
} else {
- Environment.buildPath(
+ val secondaryFile = Environment.buildPath(
context.filesDir,
ID,
userId.toString(),
FILES,
fileName
)
+ ensureParentDirExists(secondaryFile)
+ secondaryFile
}
}
@@ -114,6 +116,7 @@
fileName
)
+ ensureParentDirExists(secondaryUserDir)
return context.getSharedPreferences(secondaryUserDir, mode)
}
@@ -141,4 +144,18 @@
}
}
}
+
+ /**
+ * Checks to see if parent dir of the file exists. If it does not, we create the parent dirs
+ * recursively.
+ */
+ @VisibleForTesting
+ internal fun ensureParentDirExists(file: File) {
+ val parent = file.parentFile
+ if (!parent.exists()) {
+ if (!parent.mkdirs()) {
+ Log.e(ID, "Could not create parent directory for file: ${file.absolutePath}")
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index d414660..533c231 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -233,46 +233,29 @@
}
@Test
- public void onStart_isBroadcasting_verifyRegisterLeBroadcastServiceCallBack() {
+ public void whenBroadcasting_verifyLeBroadcastServiceCallBackIsRegisteredAndUnregistered() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
mIsBroadcasting = true;
mMediaOutputBaseDialogImpl.onStart();
-
verify(mLocalBluetoothLeBroadcast).registerServiceCallBack(any(), any());
- }
-
- @Test
- public void onStart_notBroadcasting_noRegisterLeBroadcastServiceCallBack() {
- when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
- mLocalBluetoothLeBroadcast);
- mIsBroadcasting = false;
-
- mMediaOutputBaseDialogImpl.onStart();
-
- verify(mLocalBluetoothLeBroadcast, never()).registerServiceCallBack(any(), any());
- }
-
- @Test
- public void onStart_isBroadcasting_verifyUnregisterLeBroadcastServiceCallBack() {
- when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
- mLocalBluetoothLeBroadcast);
- mIsBroadcasting = true;
mMediaOutputBaseDialogImpl.onStop();
-
verify(mLocalBluetoothLeBroadcast).unregisterServiceCallBack(any());
}
@Test
- public void onStop_notBroadcasting_noUnregisterLeBroadcastServiceCallBack() {
+ public void
+ whenNotBroadcasting_verifyLeBroadcastServiceCallBackIsNotRegisteredOrUnregistered() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
mIsBroadcasting = false;
+ mMediaOutputBaseDialogImpl.onStart();
mMediaOutputBaseDialogImpl.onStop();
+ verify(mLocalBluetoothLeBroadcast, never()).registerServiceCallBack(any(), any());
verify(mLocalBluetoothLeBroadcast, never()).unregisterServiceCallBack(any());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
index 73226fa..6d9b01e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -62,6 +63,14 @@
broadcastDispatcher, backgroundExecutor)
}
+ @After
+ fun end() {
+ val dir = Environment.buildPath(
+ context.filesDir,
+ UserFileManagerImpl.ID)
+ dir.deleteRecursively()
+ }
+
@Test
fun testGetFile() {
assertThat(userFileManager.getFile(TEST_FILE_NAME, 0).path)
@@ -72,8 +81,19 @@
@Test
fun testGetSharedPreferences() {
+ val secondarySharedPref = userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11)
+ val secondaryUserDir = Environment.buildPath(
+ context.filesDir,
+ UserFileManagerImpl.ID,
+ "11",
+ UserFileManagerImpl.SHARED_PREFS,
+ TEST_FILE_NAME
+ )
+
+ assertThat(secondarySharedPref).isNotNull()
+ assertThat(secondaryUserDir.exists())
assertThat(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 0))
- .isNotEqualTo(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11))
+ .isNotEqualTo(secondarySharedPref)
}
@Test
@@ -115,6 +135,19 @@
verify(userManager).aliveUsers
assertThat(secondaryUserDir.exists()).isFalse()
assertThat(file.exists()).isFalse()
- dir.deleteRecursively()
+ }
+
+ @Test
+ fun testEnsureParentDirExists() {
+ val file = Environment.buildPath(
+ context.filesDir,
+ UserFileManagerImpl.ID,
+ "11",
+ "files",
+ TEST_FILE_NAME
+ )
+ assertThat(file.parentFile.exists()).isFalse()
+ userFileManager.ensureParentDirExists(file)
+ assertThat(file.parentFile.exists()).isTrue()
}
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 73d9cc7..d29e25c 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -862,10 +862,10 @@
return list;
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.SHUTDOWN)
@Override
public void shutdown() {
// TODO: remove from aidl if nobody calls externally
- mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG);
Slog.i(TAG, "Shutting down");
}
@@ -1203,9 +1203,9 @@
setUidOnMeteredNetworkList(uid, true, enable);
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.NETWORK_SETTINGS)
@Override
public boolean setDataSaverModeEnabled(boolean enable) {
- mContext.enforceCallingOrSelfPermission(NETWORK_SETTINGS, TAG);
if (DBG) Log.d(TAG, "setDataSaverMode: " + enable);
synchronized (mQuotaLock) {
@@ -1741,9 +1741,9 @@
return NetdUtils.removeRoutesFromLocalNetwork(mNetdService, routes);
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
@Override
public boolean isNetworkRestricted(int uid) {
- mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
return isNetworkRestrictedInternal(uid);
}
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 7371d07..33e4070 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -564,10 +564,9 @@
return res;
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
@Override
public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) {
- mAm.mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.PACKAGE_USAGE_STATS, null);
Parcel current = Parcel.obtain();
synchronized (mLock) {
long now = SystemClock.uptimeMillis();
@@ -619,11 +618,10 @@
* @return List of proto binary of individual commit files or one that is merged from them;
* the merged, final ProcessStats object.
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
@Override
public long getCommittedStatsMerged(long highWaterMarkMs, int section, boolean doAggregate,
List<ParcelFileDescriptor> committedStats, ProcessStats mergedStats) {
- mAm.mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.PACKAGE_USAGE_STATS, null);
long newHighWaterMark = highWaterMarkMs;
mFileLock.lock();
@@ -708,10 +706,9 @@
return fds[0];
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
@Override
public ParcelFileDescriptor getStatsOverTime(long minTime) {
- mAm.mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.PACKAGE_USAGE_STATS, null);
Parcel current = Parcel.obtain();
long curTime;
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 44aefcc..fe9e68d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -897,17 +897,6 @@
throw new IllegalStateException("Injection should not result in TARGET_MISMATCH"
+ " when it is not targeted into to a specific uid.");
}
- // TODO(b/228161340): Remove the fallback of targeting injection into all windows
- // when the caller has the injection permission.
- // Explicitly maintain the same behavior as previous versions of Android, where
- // injection is allowed into all windows if the caller has the INJECT_EVENTS
- // permission, even if it is targeting a certain uid.
- if (checkCallingPermission(android.Manifest.permission.INJECT_EVENTS,
- "injectInputEvent-target-mismatch-fallback")) {
- Slog.w(TAG, "Targeted input event was not directed at a window owned by uid "
- + targetUid + ". Falling back to injecting into all windows.");
- return injectInputEventToTarget(event, mode, Process.INVALID_UID);
- }
throw new IllegalArgumentException(
"Targeted input event injection from pid " + pid
+ " was not directed at a window owned by uid "
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e29f00b..bbf9410 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3574,23 +3574,18 @@
int windowFlags, @Nullable EditorInfo editorInfo,
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
- int unverifiedTargetSdkVersion,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
- return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
- startInputFlags, softInputMode, windowFlags, editorInfo, inputConnection,
- remoteAccessibilityInputConnection, unverifiedTargetSdkVersion,
- imeDispatcher);
- }
+ if (UserHandle.getCallingUserId() != userId) {
+ mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
- @NonNull
- private InputBindResult startInputOrWindowGainedFocusInternal(
- @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
- @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
- int windowFlags, @Nullable EditorInfo editorInfo,
- @Nullable IRemoteInputConnection inputConnection,
- @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
- int unverifiedTargetSdkVersion,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+ if (editorInfo == null || editorInfo.targetInputMethodUser == null
+ || editorInfo.targetInputMethodUser.getIdentifier() != userId) {
+ throw new InvalidParameterException("EditorInfo#targetInputMethodUser must also be "
+ + "specified for cross-user startInputOrWindowGainedFocus()");
+ }
+ }
+
if (windowToken == null) {
Slog.e(TAG, "windowToken cannot be null.");
return InputBindResult.NULL;
@@ -3600,26 +3595,6 @@
"IMMS.startInputOrWindowGainedFocus");
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#startInputOrWindowGainedFocus");
- final int callingUserId = UserHandle.getCallingUserId();
- final int userId;
- if (editorInfo != null && editorInfo.targetInputMethodUser != null
- && editorInfo.targetInputMethodUser.getIdentifier() != callingUserId) {
- mContext.enforceCallingPermission(
- Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "Using EditorInfo.targetInputMethodUser requires"
- + " INTERACT_ACROSS_USERS_FULL.");
- userId = editorInfo.targetInputMethodUser.getIdentifier();
- if (!mUserManagerInternal.isUserRunning(userId)) {
- // There is a chance that we hit here because of race condition. Let's just
- // return an error code instead of crashing the caller process, which at
- // least has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an
- // important process.
- Slog.e(TAG, "User #" + userId + " is not running.");
- return InputBindResult.INVALID_USER;
- }
- } else {
- userId = callingUserId;
- }
final InputBindResult result;
synchronized (ImfLock.class) {
final long ident = Binder.clearCallingIdentity();
@@ -3668,9 +3643,19 @@
+ " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
+ " windowFlags=#" + Integer.toHexString(windowFlags)
+ " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion
+ + " userId=" + userId
+ " imeDispatcher=" + imeDispatcher);
}
+ if (!mUserManagerInternal.isUserRunning(userId)) {
+ // There is a chance that we hit here because of race condition. Let's just
+ // return an error code instead of crashing the caller process, which at
+ // least has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an
+ // important process.
+ Slog.w(TAG, "User #" + userId + " is not running.");
+ return InputBindResult.INVALID_USER;
+ }
+
final ClientState cs = mClients.get(client.asBinder());
if (cs == null) {
throw new IllegalArgumentException("unknown client " + client.asBinder());
@@ -4556,6 +4541,7 @@
}
@BinderThread
+ @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
@Override
public void startImeTrace() {
ImeTracing.getInstance().startTrace(null /* printwriter */);
@@ -4571,6 +4557,7 @@
}
@BinderThread
+ @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
@Override
public void stopImeTrace() {
ImeTracing.getInstance().stopTrace(null /* printwriter */);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index c963154..4a0a07b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -38,6 +38,7 @@
import android.app.ActivityManager.ProcessCapability;
import android.net.NetworkPolicyManager;
import android.os.UserHandle;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -79,6 +80,8 @@
private static final int EVENT_APP_IDLE_WL_CHANGED = 14;
private static final int EVENT_METERED_ALLOWLIST_CHANGED = 15;
private static final int EVENT_METERED_DENYLIST_CHANGED = 16;
+ private static final int EVENT_ROAMING_CHANGED = 17;
+ private static final int EVENT_INTERFACES_CHANGED = 18;
private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
@@ -265,6 +268,24 @@
}
}
+ void roamingChanged(int netId, boolean newRoaming) {
+ synchronized (mLock) {
+ if (LOGD || mDebugUid != INVALID_UID) {
+ Slog.d(TAG, getRoamingChangedLog(netId, newRoaming));
+ }
+ mEventsBuffer.roamingChanged(netId, newRoaming);
+ }
+ }
+
+ void interfacesChanged(int netId, ArraySet<String> newIfaces) {
+ synchronized (mLock) {
+ if (LOGD || mDebugUid != INVALID_UID) {
+ Slog.d(TAG, getInterfacesChangedLog(netId, newIfaces.toString()));
+ }
+ mEventsBuffer.interfacesChanged(netId, newIfaces.toString());
+ }
+ }
+
void setDebugUid(int uid) {
mDebugUid = uid;
}
@@ -348,6 +369,14 @@
return "metered-denylist for " + uid + " changed to " + added;
}
+ private static String getRoamingChangedLog(int netId, boolean newRoaming) {
+ return "Roaming of netId=" + netId + " changed to " + newRoaming;
+ }
+
+ private static String getInterfacesChangedLog(int netId, String newIfaces) {
+ return "Interfaces of netId=" + netId + " changed to " + newIfaces;
+ }
+
private static String getFirewallChainName(int chain) {
switch (chain) {
case FIREWALL_CHAIN_DOZABLE:
@@ -570,6 +599,28 @@
data.timeStamp = System.currentTimeMillis();
}
+ public void roamingChanged(int netId, boolean newRoaming) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_ROAMING_CHANGED;
+ data.ifield1 = netId;
+ data.bfield1 = newRoaming;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void interfacesChanged(int netId, String newIfaces) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_INTERFACES_CHANGED;
+ data.ifield1 = netId;
+ data.sfield1 = newIfaces;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
public void reverseDump(IndentingPrintWriter pw) {
final Data[] allData = toArray();
for (int i = allData.length - 1; i >= 0; --i) {
@@ -621,6 +672,10 @@
return getMeteredAllowlistChangedLog(data.ifield1, data.bfield1);
case EVENT_METERED_DENYLIST_CHANGED:
return getMeteredDenylistChangedLog(data.ifield1, data.bfield1);
+ case EVENT_ROAMING_CHANGED:
+ return getRoamingChangedLog(data.ifield1, data.bfield1);
+ case EVENT_INTERFACES_CHANGED:
+ return getInterfacesChangedLog(data.ifield1, data.sfield1);
default:
return String.valueOf(data.type);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 99a488c..de83f5a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1367,8 +1367,17 @@
final boolean roamingChanged = updateCapabilityChange(
mNetworkRoaming, newRoaming, network);
- if (meteredChanged || roamingChanged) {
+ final boolean shouldUpdateNetworkRules = meteredChanged || roamingChanged;
+
+ if (meteredChanged) {
mLogger.meterednessChanged(network.getNetId(), newMetered);
+ }
+
+ if (roamingChanged) {
+ mLogger.roamingChanged(network.getNetId(), newRoaming);
+ }
+
+ if (shouldUpdateNetworkRules) {
updateNetworkRulesNL();
}
}
@@ -1381,6 +1390,7 @@
final boolean ifacesChanged = updateNetworkToIfacesNL(network.getNetId(),
newIfaces);
if (ifacesChanged) {
+ mLogger.interfacesChanged(network.getNetId(), newIfaces);
updateNetworkRulesNL();
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f8604c0..2070c2b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6763,9 +6763,8 @@
protected void doChannelWarningToast(int forUid, CharSequence toastText) {
Binder.withCleanCallingIdentity(() -> {
- final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
final boolean warningEnabled = Settings.Global.getInt(getContext().getContentResolver(),
- Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, defaultWarningEnabled) != 0;
+ Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, 0) != 0;
if (warningEnabled) {
Toast toast = Toast.makeText(getContext(), mHandler.getLooper(), toastText,
Toast.LENGTH_SHORT);
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index b142ba6..122ab10 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -116,7 +116,7 @@
mScanFlags = scanFlags;
}
mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
- mSystemScanFlags = scanFlags | SCAN_AS_SYSTEM;
+ mSystemScanFlags = mScanFlags | SCAN_AS_SYSTEM;
mExecutorService = ParallelPackageParser.makeExecutorService();
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3f052c0..2bdf62bd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1829,8 +1829,6 @@
mAppDataHelper = new AppDataHelper(this);
mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
- mInitAppsHelper = new InitAppsHelper(this, mApexManager, mApexPackageInfo,
- mInstallPackageHelper, mInjector.getSystemPartitions());
mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
mAppDataHelper);
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
@@ -1951,6 +1949,9 @@
+ ver.fingerprint + " to " + PackagePartitions.FINGERPRINT);
}
+ mInitAppsHelper = new InitAppsHelper(this, mApexManager, mApexPackageInfo,
+ mInstallPackageHelper, mInjector.getSystemPartitions());
+
// when upgrading from pre-M, promote system app permissions from install to runtime
mPromoteSystemApps =
mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6c1f3bb..69bbff3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2728,8 +2728,13 @@
}
final StartingSurfaceController.StartingSurface surface;
- final StartingData startingData = mStartingData;
+ final boolean animate;
if (mStartingData != null) {
+ animate = prepareAnimation && mStartingData.needRevealAnimation()
+ && mStartingWindow.isVisibleByPolicy();
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Schedule remove starting %s startingWindow=%s"
+ + " animate=%b Callers=%s", this, mStartingWindow, animate,
+ Debug.getCallers(5));
surface = mStartingSurface;
mStartingData = null;
mStartingSurface = null;
@@ -2747,21 +2752,7 @@
return;
}
-
- ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Schedule remove starting %s startingWindow=%s"
- + " startingView=%s Callers=%s", this, mStartingWindow, mStartingSurface,
- Debug.getCallers(5));
-
- final Runnable removeSurface = () -> {
- ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Removing startingView=%s", surface);
- try {
- surface.remove(prepareAnimation && startingData.needRevealAnimation());
- } catch (Exception e) {
- Slog.w(TAG_WM, "Exception when removing starting window", e);
- }
- };
-
- removeSurface.run();
+ surface.remove(animate);
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 3e62a29..4066fe1 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2935,6 +2935,10 @@
newParent = candidateTf;
}
}
+ if (newParent.canHaveEmbeddingActivityTransition(mStartActivity)) {
+ // Make sure the embedded TaskFragment is included in the start activity transition.
+ newParent.collectEmbeddedTaskFragmentIfNeeded();
+ }
if (mStartActivity.getTaskFragment() == null
|| mStartActivity.getTaskFragment() == newParent) {
newParent.addChild(mStartActivity, POSITION_TOP);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 3e6546e..1c20fb3 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2316,6 +2316,26 @@
return !startBounds.equals(getBounds());
}
+ boolean canHaveEmbeddingActivityTransition(@NonNull ActivityRecord child) {
+ if (!isOrganizedTaskFragment() || !mTransitionController.isShellTransitionsEnabled()) {
+ return false;
+ }
+ // The activity should request open transition when it is becoming visible.
+ return child.isVisibleRequested();
+ }
+
+ void collectEmbeddedTaskFragmentIfNeeded() {
+ if (!isOrganizedTaskFragment() || mTransitionController.isCollecting(this)) {
+ return;
+ }
+ if (getChildCount() == 0) {
+ // The TaskFragment is new created, and just becoming non-empty.
+ mTransitionController.collectExistenceChange(this);
+ } else {
+ mTransitionController.collect(this);
+ }
+ }
+
@Override
void setSurfaceControl(SurfaceControl sc) {
super.setSurfaceControl(sc);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 91f69a5..31d8eb8 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1149,6 +1149,26 @@
return false;
}
+ private static boolean isTranslucent(@NonNull WindowContainer wc) {
+ final TaskFragment taskFragment = wc.asTaskFragment();
+ if (taskFragment != null) {
+ if (taskFragment.isTranslucent(null /* starting */)) {
+ return true;
+ }
+ final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
+ if (adjacentTaskFragment != null) {
+ // Treat the TaskFragment as translucent if its adjacent TF is, otherwise everything
+ // behind two adjacent TaskFragments are occluded.
+ return adjacentTaskFragment.isTranslucent(null /* starting */);
+ }
+ }
+ // TODO(b/172695805): hierarchical check. This is non-trivial because for containers
+ // it is effected by child visibility but needs to work even
+ // before visibility is committed. This means refactoring some
+ // checks to use requested visibility.
+ return !wc.fillsParent();
+ }
+
/**
* Under some conditions (eg. all visible targets within a parent container are transitioning
* the same way) the transition can be "promoted" to the parent container. This means an
@@ -1701,20 +1721,13 @@
if (mShowWallpaper || wc.showWallpaper()) {
flags |= FLAG_SHOW_WALLPAPER;
}
- if (!wc.fillsParent()) {
- // TODO(b/172695805): hierarchical check. This is non-trivial because for containers
- // it is effected by child visibility but needs to work even
- // before visibility is committed. This means refactoring some
- // checks to use requested visibility.
+ if (isTranslucent(wc)) {
flags |= FLAG_TRANSLUCENT;
}
final Task task = wc.asTask();
if (task != null && task.voiceSession != null) {
flags |= FLAG_IS_VOICE_INTERACTION;
}
- if (task != null && task.isTranslucent(null)) {
- flags |= FLAG_TRANSLUCENT;
- }
final ActivityRecord record = wc.asActivityRecord();
if (record != null) {
if (record.mUseTransferredAnimation) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index d979753..0f5655c 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -21,6 +21,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
@@ -843,6 +844,8 @@
break;
}
+ prepareActivityEmbeddingTransitionForReparentActivityToTaskFragment(parent,
+ activity);
activity.reparent(parent, POSITION_TOP);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
@@ -1069,6 +1072,41 @@
return effects;
}
+ private void prepareActivityEmbeddingTransitionForReparentActivityToTaskFragment(
+ @NonNull TaskFragment taskFragment, @NonNull ActivityRecord activity) {
+ if (!taskFragment.canHaveEmbeddingActivityTransition(activity)) {
+ return;
+ }
+
+ // The reparent can happen in the following cases:
+ // 1. Reparent an existing activity to split when app launches new intent.
+ // - This happens after app calls to start activity, but before the activity is actually
+ // started, so we don't expect any collecting transition, but if it does, we can't
+ // queue the WCT because the start activity won't wait.
+ // 2. Reparent an existing activity to split to launch placeholder when Task size changed.
+ // - We expect to have a collecting transition for the Task resize, so just collect.
+ // 3. Reparent a new launching activity to an always-expand container.
+ // 4. Reparent a new launching activity to split to launch placeholder together.
+ // 5. Reparent a new launching activity to an existing split.
+ // - The new launching activity should have start an OPEN transition, so just collect.
+ // 6. Reparent PiP activity back to the original Task.
+ // - This should be part of the exiting PiP transition, so just collect.
+
+ if (!taskFragment.getBounds().equals(activity.getBounds()) && activity.isVisible()
+ && !mTransitionController.isCollecting()) {
+ // 1. Reparent an existing activity to split when app launches new intent.
+ mTransitionController.requestTransitionIfNeeded(TRANSIT_CHANGE, activity);
+ }
+
+ // We expect the activity to be in the transition already, so just collect the TaskFragment.
+ if (mTransitionController.isCollecting(activity)) {
+ taskFragment.collectEmbeddedTaskFragmentIfNeeded();
+ } else {
+ ProtoLog.w(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Reparenting Activity"
+ + " to embedded TaskFragment, but the Activity is not collected");
+ }
+ }
+
/** A helper method to send minimum dimension violation error to the client. */
private void sendMinimumDimensionViolation(TaskFragment taskFragment, Point minDimensions,
IBinder errorCallbackToken, String reason) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3ef4aae..366ca3b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -137,7 +137,6 @@
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
-import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.WINDOW;
@@ -4957,10 +4956,6 @@
|| isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
}
- boolean isExitAnimationRunningSelfOrChild() {
- return isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION);
- }
-
private boolean shouldFinishAnimatingExit() {
// Exit animation might be applied soon.
if (inTransition()) {
@@ -5916,6 +5911,10 @@
if (!super.prepareSync()) {
return false;
}
+ if (mIsWallpaper) {
+ // TODO(b/233286785): Add sync support to wallpaper.
+ return false;
+ }
// In the WindowContainer implementation we immediately mark ready
// since a generic WindowContainer only needs to wait for its
// children to finish and is immediately ready from its own
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
index cdb6324..e44ce3c 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
+++ b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
@@ -34,4 +34,5 @@
],
platform_apis: true,
test_suites: ["device-tests"],
+ data: [":AppEnumerationSyncProviderTestApp"],
}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
index f48974a..cca8ec7 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
+++ b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
@@ -30,7 +30,7 @@
</target_preparer>
<!-- Load additional APKs onto device -->
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="push" value="AppEnumerationSyncProviderTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSyncProviderTestApp.apk" />
<option name="push" value="AppEnumerationHasAppOpPermissionTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationHasAppOpPermissionTestApp.apk" />
<option name="push" value="AppEnumerationSharedUserTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSharedUserTestApp.apk" />
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 60a159154..4640d36 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -88,6 +88,12 @@
@Test
override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 240238245)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 386c0cb..a9fb0f2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -83,6 +83,7 @@
transitions {
tapl.launchedAppState.quickSwitchToPreviousApp()
wmHelper.StateSyncBuilder()
+ .withFullScreenApp(testApp1)
.withNavOrTaskBarVisible()
.withStatusBarVisible()
.waitForAndVerify()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index cefcb35..3b60212 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -79,6 +79,7 @@
testApp2.launchViaIntent(wmHelper)
tapl.launchedAppState.quickSwitchToPreviousApp()
wmHelper.StateSyncBuilder()
+ .withFullScreenApp(testApp1)
.withNavOrTaskBarVisible()
.withStatusBarVisible()
.waitForAndVerify()
@@ -89,6 +90,7 @@
transitions {
tapl.launchedAppState.quickSwitchToPreviousAppSwipeLeft()
wmHelper.StateSyncBuilder()
+ .withFullScreenApp(testApp2)
.withNavOrTaskBarVisible()
.withStatusBarVisible()
.waitForAndVerify()