Merge "Ensure previous background color is reapplied when showing a SurfaceView" into rvc-dev
diff --git a/apex/Android.bp b/apex/Android.bp
index cd34f98..e8f6e6b 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -84,6 +84,24 @@
},
}
+java_defaults {
+ name: "framework-module-stubs-lib-defaults-publicapi",
+ installable: false,
+ sdk_version: "module_current",
+}
+
+java_defaults {
+ name: "framework-module-stubs-lib-defaults-systemapi",
+ installable: false,
+ sdk_version: "module_current",
+}
+
+java_defaults {
+ name: "framework-module-stubs-lib-defaults-module_libs_api",
+ installable: false,
+ sdk_version: "module_current",
+}
+
// The defaults for module_libs comes in two parts - defaults for API checks
// and defaults for stub generation. This is because we want the API txt
// files to *only* include the module_libs_api, but the stubs to include
diff --git a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
index 8fbfb1d..d99830dc4 100644
--- a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
@@ -7,6 +7,7 @@
],
"options": [
{"include-filter": "com.android.server.DeviceIdleControllerTest"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
index bc7a7d3..b76c582 100644
--- a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
@@ -4,6 +4,7 @@
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.DeviceIdleControllerTest"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
index e2e1180..484fec3 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
@@ -3,6 +3,7 @@
{
"name": "CtsJobSchedulerTestCases",
"options": [
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.LargeTest"}
]
@@ -11,6 +12,7 @@
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.job"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -18,6 +20,7 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.job"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
index ba7572a..c5dc51c 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
@@ -4,6 +4,7 @@
"name": "CtsUsageStatsTestCases",
"options": [
{"include-filter": "android.app.usage.cts.UsageStatsTest"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -11,6 +12,7 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.usage"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 821dd9e..99e82e7 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -132,19 +132,19 @@
java_library {
name: "framework-media-stubs-publicapi",
srcs: [":framework-media-stubs-srcs-publicapi"],
- sdk_version: "current",
+ defaults: ["framework-module-stubs-lib-defaults-publicapi"],
}
java_library {
name: "framework-media-stubs-systemapi",
srcs: [":framework-media-stubs-srcs-systemapi"],
- sdk_version: "system_current",
+ defaults: ["framework-module-stubs-lib-defaults-systemapi"],
}
java_library {
name: "framework-media-stubs-module_libs_api",
srcs: [":framework-media-stubs-srcs-module_libs_api"],
- sdk_version: "system_current",
+ defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
}
java_library {
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index 86f4ab7..707113b 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -86,7 +86,7 @@
java_library {
name: "framework-sdkextensions-stubs-publicapi",
srcs: [":framework-sdkextensions-stubs-srcs-publicapi"],
- sdk_version: "current",
+ defaults: ["framework-module-stubs-lib-defaults-publicapi"],
visibility: [
"//frameworks/base", // Framework
"//frameworks/base/apex/sdkextensions", // sdkextensions SDK
@@ -96,7 +96,7 @@
java_library {
name: "framework-sdkextensions-stubs-systemapi",
srcs: [":framework-sdkextensions-stubs-srcs-systemapi"],
- sdk_version: "system_current",
+ defaults: ["framework-module-stubs-lib-defaults-systemapi"],
visibility: [
"//frameworks/base", // Framework
"//frameworks/base/apex/sdkextensions", // sdkextensions SDK
@@ -106,7 +106,7 @@
java_library {
name: "framework-sdkextensions-stubs-module_libs_api",
srcs: [":framework-sdkextensions-stubs-srcs-module_libs_api"],
- sdk_version: "system_current",
+ defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
visibility: [
"//frameworks/base", // Framework
"//frameworks/base/apex/sdkextensions", // sdkextensions SDK
diff --git a/api/system-current.txt b/api/system-current.txt
index a1fd0db..7e25382 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6061,7 +6061,7 @@
method public void release();
}
- public class InvalidPacketException extends java.lang.Exception {
+ public final class InvalidPacketException extends java.lang.Exception {
ctor public InvalidPacketException(int);
method public int getError();
field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
@@ -6255,7 +6255,7 @@
field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
}
- public static class NetworkCapabilities.Builder {
+ public static final class NetworkCapabilities.Builder {
ctor public NetworkCapabilities.Builder();
ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 0bd8a19..ba6feed 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -535,6 +535,7 @@
method public void setWindowingMode(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTIVITY_TYPE_ASSISTANT = 4; // 0x4
+ field public static final int ACTIVITY_TYPE_DREAM = 5; // 0x5
field public static final int ACTIVITY_TYPE_HOME = 2; // 0x2
field public static final int ACTIVITY_TYPE_RECENTS = 3; // 0x3
field public static final int ACTIVITY_TYPE_STANDARD = 1; // 0x1
@@ -1833,7 +1834,7 @@
field public static final int TRANSPORT_TEST = 7; // 0x7
}
- public static class NetworkCapabilities.Builder {
+ public static final class NetworkCapabilities.Builder {
ctor public NetworkCapabilities.Builder();
ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bd3fee2d..21b56d3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4974,10 +4974,8 @@
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
- Class<? extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
if (r != null) {
- activityClass = r.activity.getClass();
r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
@@ -5030,7 +5028,6 @@
synchronized (mResourcesManager) {
mActivities.remove(token);
}
- StrictMode.decrementExpectedActivityCount(activityClass);
return r;
}
@@ -5050,6 +5047,7 @@
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance, reason);
if (r != null) {
+ Class<? extends Activity> activityClass = r.activity.getClass();
cleanUpPendingRemoveWindows(r, finishing);
WindowManager wm = r.activity.getWindowManager();
View v = r.activity.mDecor;
@@ -5074,14 +5072,14 @@
}
if (wtoken != null && r.mPendingRemoveWindow == null) {
WindowManagerGlobal.getInstance().closeAll(wtoken,
- r.activity.getClass().getName(), "Activity");
+ activityClass.getName(), "Activity");
} else if (r.mPendingRemoveWindow != null) {
// We're preserving only one window, others should be closed so app views
// will be detached before the final tear down. It should be done now because
// some components (e.g. WebView) rely on detach callbacks to perform receiver
// unregister and other cleanup.
WindowManagerGlobal.getInstance().closeAllExceptView(token, v,
- r.activity.getClass().getName(), "Activity");
+ activityClass.getName(), "Activity");
}
r.activity.mDecor = null;
}
@@ -5093,18 +5091,23 @@
// about leaking windows, because that is a bug, so if they are
// using this recreate facility then they get to live with leaks.
WindowManagerGlobal.getInstance().closeAll(token,
- r.activity.getClass().getName(), "Activity");
+ activityClass.getName(), "Activity");
}
// Mocked out contexts won't be participating in the normal
// process lifecycle, but if we're running with a proper
// ApplicationContext we need to have it tear down things
// cleanly.
- Context c = r.activity.getBaseContext();
- if (c instanceof ContextImpl) {
- ((ContextImpl) c).scheduleFinalCleanup(
- r.activity.getClass().getName(), "Activity");
+ final ContextImpl impl = ContextImpl.getImpl(r.activity);
+ if (impl != null) {
+ impl.scheduleFinalCleanup(activityClass.getName(), "Activity");
}
+
+ r.activity = null;
+ r.window = null;
+ r.hideForNow = false;
+ r.nextIdle = null;
+ StrictMode.decrementExpectedActivityCount(activityClass);
}
if (finishing) {
try {
@@ -5334,10 +5337,6 @@
handleDestroyActivity(r.token, false, configChanges, true, reason);
- r.activity = null;
- r.window = null;
- r.hideForNow = false;
- r.nextIdle = null;
// Merge any pending results and pending intents; don't just replace them
if (pendingResults != null) {
if (r.pendingResults == null) {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index f590eb9..78d3581 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -48,8 +48,6 @@
void clearData(String pkg, int uid, boolean fromApp);
void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, int displayId, @nullable ITransientNotificationCallback callback);
void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId);
- // TODO(b/144152069): Remove this after assessing impact on dogfood.
- void enqueueTextOrCustomToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId, boolean isCustom);
void cancelToast(String pkg, IBinder token);
void finishToken(String pkg, IBinder token);
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 6b40890..37e07de 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -141,6 +141,8 @@
public static final int ACTIVITY_TYPE_RECENTS = 3;
/** Assistant activity type. */
public static final int ACTIVITY_TYPE_ASSISTANT = 4;
+ /** Dream activity type. */
+ public static final int ACTIVITY_TYPE_DREAM = 5;
/** @hide */
@IntDef(prefix = { "ACTIVITY_TYPE_" }, value = {
@@ -149,6 +151,7 @@
ACTIVITY_TYPE_HOME,
ACTIVITY_TYPE_RECENTS,
ACTIVITY_TYPE_ASSISTANT,
+ ACTIVITY_TYPE_DREAM,
})
public @interface ActivityType {}
@@ -746,9 +749,11 @@
* @hide
*/
public boolean isAlwaysOnTop() {
- return mWindowingMode == WINDOWING_MODE_PINNED || (mAlwaysOnTop == ALWAYS_ON_TOP_ON
- && (mWindowingMode == WINDOWING_MODE_FREEFORM
- || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW));
+ if (mWindowingMode == WINDOWING_MODE_PINNED) return true;
+ if (mActivityType == ACTIVITY_TYPE_DREAM) return true;
+ if (mAlwaysOnTop != ALWAYS_ON_TOP_ON) return false;
+ return mWindowingMode == WINDOWING_MODE_FREEFORM
+ || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW;
}
/**
@@ -798,7 +803,7 @@
/** @hide */
public static boolean supportSplitScreenWindowingMode(int activityType) {
- return activityType != ACTIVITY_TYPE_ASSISTANT;
+ return activityType != ACTIVITY_TYPE_ASSISTANT && activityType != ACTIVITY_TYPE_DREAM;
}
/** @hide */
@@ -823,6 +828,7 @@
case ACTIVITY_TYPE_HOME: return "home";
case ACTIVITY_TYPE_RECENTS: return "recents";
case ACTIVITY_TYPE_ASSISTANT: return "assistant";
+ case ACTIVITY_TYPE_DREAM: return "dream";
}
return String.valueOf(applicationType);
}
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index e0674d7..fa62a02 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -379,6 +379,7 @@
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+ verifyDeviceNotNull(device, "setConnectionPolicy");
final IBluetoothHearingAid service = getService();
try {
if (service != null && isEnabled()
@@ -428,6 +429,7 @@
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
+ verifyDeviceNotNull(device, "getConnectionPolicy");
final IBluetoothHearingAid service = getService();
try {
if (service != null && isEnabled()
@@ -504,6 +506,7 @@
if (VDBG) {
log("getHiSyncId(" + device + ")");
}
+ verifyDeviceNotNull(device, "getConnectionPolicy");
final IBluetoothHearingAid service = getService();
try {
if (service == null) {
@@ -577,6 +580,13 @@
return false;
}
+ private void verifyDeviceNotNull(BluetoothDevice device, String methodName) {
+ if (device == null) {
+ Log.e(TAG, methodName + ": device param is null");
+ throw new IllegalArgumentException("Device cannot be null");
+ }
+ }
+
private boolean isValidDevice(BluetoothDevice device) {
if (device == null) return false;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 95385ee..b1d6c83 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -11215,6 +11215,17 @@
&& hasWebURI();
}
+ private boolean isImageCaptureIntent() {
+ return (MediaStore.ACTION_IMAGE_CAPTURE.equals(mAction)
+ || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(mAction)
+ || MediaStore.ACTION_VIDEO_CAPTURE.equals(mAction));
+ }
+
+ /** @hide */
+ public boolean isImplicitImageCaptureIntent() {
+ return mPackage == null && mComponent == null && isImageCaptureIntent();
+ }
+
/**
* @hide
*/
@@ -11241,9 +11252,7 @@
}
putParcelableArrayListExtra(EXTRA_STREAM, newStreams);
}
- } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
- || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action)
- || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) {
+ } else if (isImageCaptureIntent()) {
final Uri output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
if (output != null) {
putExtra(MediaStore.EXTRA_OUTPUT, maybeAddUserId(output, contentUserHint));
@@ -11349,9 +11358,7 @@
}
} catch (ClassCastException e) {
}
- } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
- || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action)
- || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) {
+ } else if (isImageCaptureIntent()) {
final Uri output;
try {
output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
index c31cb4e..9b3e8c9 100644
--- a/core/java/android/inputmethodservice/InlineSuggestionSession.java
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -166,9 +166,14 @@
}
return;
}
-
+ // The IME doesn't have information about the virtual view id for the child views in the
+ // web view, so we are only comparing the parent view id here. This means that for cases
+ // where there are two input fields in the web view, they will have the same view id
+ // (although different virtual child id), and we will not be able to distinguish them.
+ final AutofillId imeClientFieldId = mClientAutofillIdSupplier.get();
if (!mComponentName.getPackageName().equals(mClientPackageNameSupplier.get())
- || !fieldId.equalsIgnoreSession(mClientAutofillIdSupplier.get())) {
+ || imeClientFieldId == null
+ || fieldId.getViewId() != imeClientFieldId.getViewId()) {
if (DEBUG) {
Log.d(TAG,
"handleOnInlineSuggestionsResponse() called on the wrong package/field "
diff --git a/core/java/android/net/InvalidPacketException.java b/core/java/android/net/InvalidPacketException.java
index b3b0f11..1873d77 100644
--- a/core/java/android/net/InvalidPacketException.java
+++ b/core/java/android/net/InvalidPacketException.java
@@ -27,7 +27,7 @@
* @hide
*/
@SystemApi
-public class InvalidPacketException extends Exception {
+public final class InvalidPacketException extends Exception {
private final int mError;
// Must match SocketKeepalive#ERROR_INVALID_IP_ADDRESS.
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index fcfcebd..af9414c 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -415,6 +415,20 @@
| (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
/**
+ * Capabilities that are allowed for test networks. This list must be set so that it is safe
+ * for an unprivileged user to create a network with these capabilities via shell. As such,
+ * it must never contain capabilities that are generally useful to the system, such as
+ * INTERNET, IMS, SUPL, etc.
+ */
+ private static final long TEST_NETWORKS_ALLOWED_CAPABILITIES =
+ (1 << NET_CAPABILITY_NOT_METERED)
+ | (1 << NET_CAPABILITY_NOT_RESTRICTED)
+ | (1 << NET_CAPABILITY_NOT_VPN)
+ | (1 << NET_CAPABILITY_NOT_ROAMING)
+ | (1 << NET_CAPABILITY_NOT_CONGESTED)
+ | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+
+ /**
* Adds the given capability to this {@code NetworkCapability} instance.
* Note that when searching for a network to satisfy a request, all capabilities
* requested must be satisfied.
@@ -646,6 +660,21 @@
}
/**
+ * Test networks have strong restrictions on what capabilities they can have. Enforce these
+ * restrictions.
+ * @hide
+ */
+ public void restrictCapabilitesForTestNetwork() {
+ final long originalCapabilities = mNetworkCapabilities;
+ final NetworkSpecifier originalSpecifier = mNetworkSpecifier;
+ clearAll();
+ // Reset the transports to only contain TRANSPORT_TEST.
+ mTransportTypes = (1 << TRANSPORT_TEST);
+ mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
+ mNetworkSpecifier = originalSpecifier;
+ }
+
+ /**
* Representing the transport type. Apps should generally not care about transport. A
* request for a fast internet connection could be satisfied by a number of different
* transports. If any are specified here it will be satisfied a Network that matches
@@ -2000,7 +2029,7 @@
*/
@SystemApi
@TestApi
- public static class Builder {
+ public static final class Builder {
private final NetworkCapabilities mCaps;
/**
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index bbc936d..99bdfd1 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -194,4 +194,5 @@
boolean needsCheckpoint() = 86;
void abortChanges(in String message, boolean retry) = 87;
void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88;
+ void fixupAppDir(in String path) = 89;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 1454aac..a68cc3d 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2470,6 +2470,36 @@
}
}
+ /**
+ * Asks StorageManager to fixup the permissions of an application-private directory.
+ *
+ * On devices without sdcardfs, filesystem permissions aren't magically fixed up. This
+ * is problematic mostly in application-private directories, which are owned by the
+ * application itself; if another process with elevated permissions creates a file
+ * in these directories, the UID will be wrong, and the owning package won't be able
+ * to access the files.
+ *
+ * This API can be used to recursively fix up the permissions on the passed in path.
+ * The default platform user of this API is the DownloadProvider, which can download
+ * things in application-private directories on their behalf.
+ *
+ * This API doesn't require any special permissions, because it merely changes the
+ * permissions of a directory to what they should anyway be.
+ *
+ * @param path the path for which we should fix up the permissions
+ *
+ * @hide
+ */
+ public void fixupAppDir(@NonNull File path) {
+ try {
+ mStorageManager.fixupAppDir(path.getCanonicalPath());
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to get canonical path for " + path.getPath(), e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** {@hide} */
private static void setCacheBehavior(File path, String name, boolean enabled)
throws IOException {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4aeea10..62dd192 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -405,6 +405,13 @@
// The actual zoom value may changes based on this initial zoom value.
private float mInitialZoom = 1f;
+ // For calculating the line change slops while moving cursor/selection.
+ // The slop max/min value include line height and the slop on the upper/lower line.
+ private static final int LINE_CHANGE_SLOP_MAX_DP = 45;
+ private static final int LINE_CHANGE_SLOP_MIN_DP = 12;
+ private int mLineChangeSlopMax;
+ private int mLineChangeSlopMin;
+
Editor(TextView textView) {
mTextView = textView;
// Synchronize the filter list, which places the undo input filter at the end.
@@ -430,6 +437,14 @@
logCursor("Editor", "New magnifier is %s.",
mNewMagnifierEnabled ? "enabled" : "disabled");
}
+
+ mLineChangeSlopMax = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, LINE_CHANGE_SLOP_MAX_DP,
+ mTextView.getContext().getResources().getDisplayMetrics());
+ mLineChangeSlopMin = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, LINE_CHANGE_SLOP_MIN_DP,
+ mTextView.getContext().getResources().getDisplayMetrics());
+
}
@VisibleForTesting
@@ -6018,7 +6033,14 @@
}
}
- private int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
+ @VisibleForTesting
+ public void setLineChangeSlopMinMaxForTesting(final int min, final int max) {
+ mLineChangeSlopMin = min;
+ mLineChangeSlopMax = max;
+ }
+
+ @VisibleForTesting
+ public int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
final int trueLine = mTextView.getLineAtCoordinate(y);
if (layout == null || prevLine > layout.getLineCount()
|| layout.getLineCount() <= 0 || prevLine < 0) {
@@ -6031,28 +6053,21 @@
return trueLine;
}
+ final int lineHeight = layout.getLineBottom(prevLine) - layout.getLineTop(prevLine);
+ int slop = (int)(LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS
+ * (layout.getLineBottom(trueLine) - layout.getLineTop(trueLine)));
+ slop = Math.max(mLineChangeSlopMin,
+ Math.min(mLineChangeSlopMax, lineHeight + slop)) - lineHeight;
+ slop = Math.max(0, slop);
+
final float verticalOffset = mTextView.viewportToContentVerticalOffset();
- final int lineCount = layout.getLineCount();
- final float slop = mTextView.getLineHeight() * LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS;
-
- final float firstLineTop = layout.getLineTop(0) + verticalOffset;
- final float prevLineTop = layout.getLineTop(prevLine) + verticalOffset;
- final float yTopBound = Math.max(prevLineTop - slop, firstLineTop + slop);
-
- final float lastLineBottom = layout.getLineBottom(lineCount - 1) + verticalOffset;
- final float prevLineBottom = layout.getLineBottom(prevLine) + verticalOffset;
- final float yBottomBound = Math.min(prevLineBottom + slop, lastLineBottom - slop);
-
- // Determine if we've moved lines based on y position and previous line.
- int currLine;
- if (y <= yTopBound) {
- currLine = Math.max(prevLine - 1, 0);
- } else if (y >= yBottomBound) {
- currLine = Math.min(prevLine + 1, lineCount - 1);
- } else {
- currLine = prevLine;
+ if (trueLine > prevLine && y >= layout.getLineBottom(prevLine) + slop + verticalOffset) {
+ return trueLine;
}
- return currLine;
+ if (trueLine < prevLine && y <= layout.getLineTop(prevLine) - slop + verticalOffset) {
+ return trueLine;
+ }
+ return prevLine;
}
/**
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 8943da4..4f14539 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -144,9 +144,6 @@
@Nullable
private CharSequence mText;
- // TODO(b/144152069): Remove this after assessing impact on dogfood.
- private boolean mIsCustomToast;
-
/**
* Construct an empty Toast object. You must call {@link #setView} before you
* can call {@link #show}.
@@ -214,8 +211,7 @@
service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback);
}
} else {
- service.enqueueTextOrCustomToast(pkg, mToken, tn, mDuration, displayId,
- mIsCustomToast);
+ service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
}
} catch (RemoteException e) {
// Empty
@@ -253,7 +249,6 @@
*/
@Deprecated
public void setView(View view) {
- mIsCustomToast = true;
mNextView = view;
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index c83cab7..31527e8 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -227,7 +227,7 @@
}
final int oldCollapsibleHeight = mCollapsibleHeight;
- mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight());
+ mCollapsibleHeight = Math.min(mCollapsibleHeight, getMaxCollapsedHeight());
if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) {
return;
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index ed2c5b2..ab57e3d 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2665,4 +2665,8 @@
// Open: Settings > Sound > Do Not Disturb > People > Messages
// OS: R
DND_MESSAGES = 1839;
+
+ // Open: Settings > Sound > Do Not Disturb > Apps > <Choose App>
+ // OS: R
+ DND_APPS_BYPASSING = 1840;
}
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 2ef0c92..88f9fc2 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -702,7 +702,7 @@
</style>
<style name="Theme.Dream">
- <item name="windowBackground">@null</item>
+ <item name="windowBackground">@color/black</item>
<item name="windowDisablePreview">true</item>
</style>
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index f81964c..1b5ce8fd 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -16,6 +16,7 @@
package android.widget;
+import static android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE;
import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.dragOnText;
import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
@@ -37,6 +38,9 @@
import android.app.Instrumentation;
import android.graphics.Rect;
import android.text.Layout;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.AbsoluteSizeSpan;
import android.util.ArraySet;
import android.util.Log;
import android.view.InputDevice;
@@ -489,6 +493,38 @@
}
@Test
+ public void testLineChangeSlop() throws Throwable {
+ TextView tv = mActivity.findViewById(R.id.textview);
+ Spannable s = new SpannableString("a\nb\nc");
+ s.setSpan(new AbsoluteSizeSpan(10), 2, 4, SPAN_INCLUSIVE_EXCLUSIVE);
+ s.setSpan(new AbsoluteSizeSpan(32), 4, 5, SPAN_INCLUSIVE_EXCLUSIVE);
+ mInstrumentation.runOnMainSync(() -> tv.setText(s));
+
+ Layout layout = tv.getLayout();
+ Editor editor = tv.getEditorForTesting();
+ final float verticalOffset = tv.getExtendedPaddingTop();
+ editor.setLineChangeSlopMinMaxForTesting(30, 65);
+ // Hit top part of upper line, jump to upper line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 5 + verticalOffset))
+ .isEqualTo(0);
+ // Hit bottom part of upper line, stay at current line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 40 + verticalOffset))
+ .isEqualTo(1);
+ // Hit current line, stay at current line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 70 + verticalOffset))
+ .isEqualTo(1);
+ // Hit top part of lower line, stay at current line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 85 + verticalOffset))
+ .isEqualTo(1);
+ // Hit bottom part of lower line, jump to lower line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 110 + verticalOffset))
+ .isEqualTo(2);
+ // Hit lower line of lower line, jump to target line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 0, 110 + verticalOffset))
+ .isEqualTo(2);
+ }
+
+ @Test
public void testCursorDrag_snapDistance() throws Throwable {
String text = "line1: This is the 1st line: A\n"
+ "line2: This is the 2nd line: B\n"
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index 5c89da0..98f5824 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -61,7 +61,6 @@
<hidden-api-whitelisted-app package="com.android.terminal" />
<hidden-api-whitelisted-app package="com.android.wallpaper" />
<hidden-api-whitelisted-app package="jp.co.omronsoft.openwnn" />
- <!-- STOPSHIP: Remove this when fixing all @hide usage for tethering.-->
- <hidden-api-whitelisted-app package="com.android.networkstack.tethering" />
+ <!-- TODO: Remove NetworkStack whitelisting -->
<hidden-api-whitelisted-app package="com.android.networkstack" />
</config>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index d17f242..a1fba4a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -174,7 +174,7 @@
@Override
public boolean isEnabled(BluetoothDevice device) {
- if (mService == null) {
+ if (mService == null || device == null) {
return false;
}
return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
@@ -182,7 +182,7 @@
@Override
public int getConnectionPolicy(BluetoothDevice device) {
- if (mService == null) {
+ if (mService == null || device == null) {
return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
@@ -191,7 +191,7 @@
@Override
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
boolean isEnabled = false;
- if (mService == null) {
+ if (mService == null || device == null) {
return false;
}
if (enabled) {
@@ -213,7 +213,7 @@
}
public long getHiSyncId(BluetoothDevice device) {
- if (mService == null) {
+ if (mService == null || device == null) {
return BluetoothHearingAid.HI_SYNC_ID_INVALID;
}
return mService.getHiSyncId(device);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 07bd3a0..1369350 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -73,14 +73,19 @@
public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED = 1 << 9;
// The search feature is disabled (either by SUW/SysUI/device policy)
public static final int SYSUI_STATE_SEARCH_DISABLED = 1 << 10;
- // The notification panel is expanded and interactive (either locked or unlocked), and the
- // quick settings is not expanded
+ // The notification panel is expanded and interactive (either locked or unlocked), and quick
+ // settings is expanded.
public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
// Winscope tracing is enabled
public static final int SYSUI_STATE_TRACING_ENABLED = 1 << 12;
// The Assistant gesture should be constrained. It is up to the launcher implementation to
// decide how to constrain it
public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13;
+ // The bubble stack is expanded. This means that the home gesture should be ignored, since a
+ // swipe up is an attempt to close the bubble stack, but that the back gesture should remain
+ // enabled (since it's used to navigate back within the bubbled app, or to collapse the bubble
+ // stack.
+ public static final int SYSUI_STATE_BUBBLES_EXPANDED = 1 << 14;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -96,7 +101,8 @@
SYSUI_STATE_HOME_DISABLED,
SYSUI_STATE_SEARCH_DISABLED,
SYSUI_STATE_TRACING_ENABLED,
- SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED
+ SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
+ SYSUI_STATE_BUBBLES_EXPANDED
})
public @interface SystemUiStateFlags {}
@@ -118,6 +124,7 @@
str.add((flags & SYSUI_STATE_TRACING_ENABLED) != 0 ? "tracing" : "");
str.add((flags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0
? "asst_gesture_constrain" : "");
+ str.add((flags & SYSUI_STATE_BUBBLES_EXPANDED) != 0 ? "bubbles_expanded" : "");
return str.toString();
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 31f90c6..01c2faa 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -74,6 +74,7 @@
import com.android.systemui.R;
import com.android.systemui.bubbles.dagger.BubbleModule;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PinnedStackListenerForwarder;
@@ -174,6 +175,7 @@
private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private IStatusBarService mBarService;
+ private SysUiState mSysUiState;
// Used for determining view rect for touch interaction
private Rect mTempRect = new Rect();
@@ -290,11 +292,12 @@
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
DumpManager dumpManager,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState) {
this(context, notificationShadeWindowController, statusBarStateController, shadeController,
data, null /* synchronizer */, configurationController, interruptionStateProvider,
zenModeController, notifUserManager, groupManager, entryManager,
- notifPipeline, featureFlags, dumpManager, floatingContentCoordinator);
+ notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState);
}
/**
@@ -315,7 +318,8 @@
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
DumpManager dumpManager,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState) {
dumpManager.registerDumpable(TAG, this);
mContext = context;
mShadeController = shadeController;
@@ -340,6 +344,7 @@
});
configurationController.addCallback(this /* configurationListener */);
+ mSysUiState = sysUiState;
mBubbleData = data;
mBubbleData.setListener(mBubbleDataListener);
@@ -593,7 +598,8 @@
private void ensureStackViewCreated() {
if (mStackView == null) {
mStackView = new BubbleStackView(
- mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator);
+ mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator,
+ mSysUiState);
ViewGroup nsv = mNotificationShadeWindowController.getNotificationShadeView();
int bubbleScrimIndex = nsv.indexOfChild(nsv.findViewById(R.id.scrim_for_bubble));
int stackIndex = bubbleScrimIndex + 1; // Show stack above bubble scrim.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 541c8cf..7191a20 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -80,6 +80,8 @@
import com.android.systemui.bubbles.animation.ExpandedAnimationController;
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
import com.android.systemui.bubbles.animation.StackAnimationController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
@@ -241,6 +243,7 @@
private BubbleTouchHandler mTouchHandler;
private BubbleController.BubbleExpandListener mExpandListener;
+ private SysUiState mSysUiState;
private boolean mViewUpdatedRequested = false;
private boolean mIsExpansionAnimating = false;
@@ -437,7 +440,8 @@
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState) {
super(context);
mBubbleData = data;
@@ -445,6 +449,8 @@
mTouchHandler = new BubbleTouchHandler(this, data, context);
setOnTouchListener(mTouchHandler);
+ mSysUiState = sysUiState;
+
Resources res = getResources();
mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
@@ -1055,6 +1061,11 @@
if (shouldExpand == mIsExpanded) {
return;
}
+
+ mSysUiState
+ .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
+ .commitUpdate(mContext.getDisplayId());
+
if (mIsExpanded) {
animateCollapse();
logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index 27c9e98..e84e932 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -21,6 +21,7 @@
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubbleData;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -62,7 +63,8 @@
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
DumpManager dumpManager,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState) {
return new BubbleController(
context,
notificationShadeWindowController,
@@ -79,6 +81,7 @@
notifPipeline,
featureFlags,
dumpManager,
- floatingContentCoordinator);
+ floatingContentCoordinator,
+ sysUiState);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 3879c16..1aa7831 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -25,6 +25,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -129,6 +130,7 @@
private int mDividerInsets;
private final Display mDefaultDisplay;
+ private boolean mSupportSplitScreenMultiWindow;
private int mDividerSize;
private int mTouchElevation;
@@ -282,6 +284,8 @@
final DisplayManager displayManager =
(DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ mSupportSplitScreenMultiWindow =
+ ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
}
@Override
@@ -354,6 +358,11 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (!mSupportSplitScreenMultiWindow) {
+ super.onLayout(changed, left, top, right, bottom);
+ return;
+ }
+
if (mFirstLayout) {
// Wait for first layout so that the ViewRootImpl surface has been created.
initializeSurfaceState();
@@ -1085,6 +1094,13 @@
crop.offsetTo(-(otherTaskRect.left - otherRect.left),
-(otherTaskRect.top - otherRect.top));
t.setWindowCrop(mTiles.mSecondarySurface, crop);
+ // Reposition home and recents surfaces or they would be positioned relatively to its
+ // parent (split-screen secondary task) position.
+ for (int i = mTiles.mHomeAndRecentsSurfaces.size() - 1; i >= 0; --i) {
+ t.setPosition(mTiles.mHomeAndRecentsSurfaces.get(i),
+ mTiles.mHomeBounds.left - otherTaskRect.left,
+ mTiles.mHomeBounds.top - otherTaskRect.top);
+ }
final SurfaceControl dividerCtrl = getWindowSurfaceControl();
if (dividerCtrl != null) {
if (isHorizontalDivision()) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index c4089e5..6cb7f4f 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -26,12 +26,15 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
+import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
-import android.window.ITaskOrganizer;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.window.ITaskOrganizer;
+
+import java.util.ArrayList;
class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub {
private static final String TAG = "SplitScreenTaskOrganizer";
@@ -43,6 +46,8 @@
SurfaceControl mSecondarySurface;
SurfaceControl mPrimaryDim;
SurfaceControl mSecondaryDim;
+ ArrayList<SurfaceControl> mHomeAndRecentsSurfaces = new ArrayList<>();
+ Rect mHomeBounds = new Rect();
final Divider mDivider;
SplitScreenTaskOrganizer(Divider divider) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 8724e49..6ed7afe 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -29,9 +29,9 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
+import android.view.WindowManagerGlobal;
import android.window.IWindowContainer;
import android.window.WindowContainerTransaction;
-import android.view.WindowManagerGlobal;
import android.window.WindowOrganizer;
import com.android.internal.annotations.GuardedBy;
@@ -157,6 +157,7 @@
for (int i = homeStacks.size() - 1; i >= 0; --i) {
wct.setBounds(homeStacks.get(i), homeBounds);
}
+ layout.mTiles.mHomeBounds.set(homeBounds);
return isHomeResizable;
}
@@ -180,13 +181,17 @@
if (rootTasks.isEmpty()) {
return false;
}
+ tiles.mHomeAndRecentsSurfaces.clear();
for (int i = rootTasks.size() - 1; i >= 0; --i) {
- if (rootTasks.get(i).configuration.windowConfiguration.getWindowingMode()
+ final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
+ if (isHomeOrRecentTask(rootTask)) {
+ tiles.mHomeAndRecentsSurfaces.add(rootTask.token.getLeash());
+ }
+ if (rootTask.configuration.windowConfiguration.getWindowingMode()
!= WINDOWING_MODE_FULLSCREEN) {
continue;
}
- wct.reparent(rootTasks.get(i).token, tiles.mSecondary.token,
- true /* onTop */);
+ wct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */);
}
boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct);
WindowOrganizer.applyTransaction(wct);
@@ -213,6 +218,7 @@
// Set launch root first so that any task created after getChildContainers and
// before reparent (pretty unlikely) are put into fullscreen.
TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null);
+ tiles.mHomeAndRecentsSurfaces.clear();
// TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
// plus specific APIs to clean this up.
List<ActivityManager.RunningTaskInfo> primaryChildren =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index cf9d43e..d70484e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-
import android.annotation.IntDef;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -30,241 +28,98 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.view.ViewTreeObserver;
-import android.view.accessibility.AccessibilityNodeInfo;
+import android.util.SparseArray;
+import android.view.ViewTreeObserver.OnPreDrawListener;
import com.android.internal.graphics.ColorUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.KeyguardAffordanceView;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility;
-import com.android.systemui.statusbar.policy.AccessibilityController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import javax.inject.Inject;
-import javax.inject.Named;
-
/**
* Manages the different states and animations of the unlock icon.
*/
-public class LockIcon extends KeyguardAffordanceView implements
- ViewTreeObserver.OnPreDrawListener {
+public class LockIcon extends KeyguardAffordanceView {
- private static final int STATE_LOCKED = 0;
- private static final int STATE_LOCK_OPEN = 1;
- private static final int STATE_SCANNING_FACE = 2;
- private static final int STATE_BIOMETRICS_ERROR = 3;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final AccessibilityController mAccessibilityController;
- private final KeyguardStateController mKeyguardStateController;
- private final KeyguardBypassController mBypassController;
- private final NotificationWakeUpCoordinator mWakeUpCoordinator;
- private final HeadsUpManagerPhone mHeadsUpManager;
-
- private int mLastState = 0;
- private boolean mForceUpdate;
- private boolean mTransientBiometricsError;
- private boolean mIsFaceUnlockState;
- private boolean mSimLocked;
- private int mDensity;
+ static final int STATE_LOCKED = 0;
+ static final int STATE_LOCK_OPEN = 1;
+ static final int STATE_SCANNING_FACE = 2;
+ static final int STATE_BIOMETRICS_ERROR = 3;
+ private float mDozeAmount;
+ private int mIconColor;
+ private StateProvider mStateProvider;
+ private int mOldState;
private boolean mPulsing;
private boolean mDozing;
- private boolean mDocked;
- private boolean mBlockUpdates;
- private int mIconColor;
- private float mDozeAmount;
- private boolean mBouncerShowingScrimmed;
- private boolean mWakeAndUnlockRunning;
- private boolean mKeyguardShowing;
- private boolean mShowingLaunchAffordance;
private boolean mKeyguardJustShown;
- private boolean mUpdatePending;
- private boolean mBouncerPreHideAnimation;
- private int mStatusBarState = StatusBarState.SHADE;
+ private final SparseArray<Drawable> mDrawableCache = new SparseArray<>();
- private final KeyguardStateController.Callback mKeyguardMonitorCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardShowingChanged() {
- boolean force = false;
- boolean wasShowing = mKeyguardShowing;
- mKeyguardShowing = mKeyguardStateController.isShowing();
- if (!wasShowing && mKeyguardShowing && mBlockUpdates) {
- mBlockUpdates = false;
- force = true;
- }
- if (!wasShowing && mKeyguardShowing) {
- mKeyguardJustShown = true;
- }
- update(force);
- }
+ private final OnPreDrawListener mOnPreDrawListener = new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getViewTreeObserver().removeOnPreDrawListener(this);
- @Override
- public void onKeyguardFadingAwayChanged() {
- if (!mKeyguardStateController.isKeyguardFadingAway()) {
- mBouncerPreHideAnimation = false;
- if (mBlockUpdates) {
- mBlockUpdates = false;
- update(true /* force */);
- }
- }
- }
+ int newState = mStateProvider.getState();
+ Drawable icon = getIcon(newState);
+ setImageDrawable(icon, false);
- @Override
- public void onUnlockedChanged() {
- update();
- }
- };
+ if (newState == STATE_SCANNING_FACE) {
+ announceForAccessibility(getResources().getString(
+ R.string.accessibility_scanning_face));
+ }
- @Inject
- public LockIcon(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
- AccessibilityController accessibilityController,
- KeyguardBypassController bypassController,
- NotificationWakeUpCoordinator wakeUpCoordinator,
- KeyguardStateController keyguardStateController,
- HeadsUpManagerPhone headsUpManager) {
+ if (icon instanceof AnimatedVectorDrawable) {
+ final AnimatedVectorDrawable animation = (AnimatedVectorDrawable) icon;
+ animation.forceAnimationOnUI();
+ animation.clearAnimationCallbacks();
+ animation.registerAnimationCallback(
+ new Animatable2.AnimationCallback() {
+ @Override
+ public void onAnimationEnd(Drawable drawable) {
+ if (getDrawable() == animation
+ && newState == mStateProvider.getState()
+ && newState == STATE_SCANNING_FACE) {
+ animation.start();
+ } else {
+ Trace.endAsyncSection("LockIcon#Animation", newState);
+ }
+ }
+ });
+ Trace.beginAsyncSection("LockIcon#Animation", newState);
+ animation.start();
+ }
+
+ return true;
+ }
+ };
+
+ public LockIcon(Context context, AttributeSet attrs) {
super(context, attrs);
- mContext = context;
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- mAccessibilityController = accessibilityController;
- mBypassController = bypassController;
- mWakeUpCoordinator = wakeUpCoordinator;
- mKeyguardStateController = keyguardStateController;
- mHeadsUpManager = headsUpManager;
}
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
- mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
- update();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
- }
-
- /**
- * If we're currently presenting an authentication error message.
- */
- public void setTransientBiometricsError(boolean transientBiometricsError) {
- mTransientBiometricsError = transientBiometricsError;
- update();
+ void setStateProvider(StateProvider stateProvider) {
+ mStateProvider = stateProvider;
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- final int density = newConfig.densityDpi;
- if (density != mDensity) {
- mDensity = density;
- update();
- }
- }
-
- public void update() {
- update(false /* force */);
- }
-
- public void update(boolean force) {
- if (force) {
- mForceUpdate = true;
- }
- if (!mUpdatePending) {
- mUpdatePending = true;
- getViewTreeObserver().addOnPreDrawListener(this);
- }
- }
-
- @Override
- public boolean onPreDraw() {
- mUpdatePending = false;
- getViewTreeObserver().removeOnPreDrawListener(this);
-
- int state = getState();
- int lastState = mLastState;
- boolean keyguardJustShown = mKeyguardJustShown;
- mIsFaceUnlockState = state == STATE_SCANNING_FACE;
- mLastState = state;
- mKeyguardJustShown = false;
-
- boolean shouldUpdate = lastState != state || mForceUpdate;
- if (mBlockUpdates && canBlockUpdates()) {
- shouldUpdate = false;
- }
- if (shouldUpdate) {
- mForceUpdate = false;
- @LockAnimIndex final int lockAnimIndex = getAnimationIndexForTransition(lastState,
- state, mPulsing, mDozing, keyguardJustShown);
- boolean isAnim = lockAnimIndex != -1;
- int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(state);
-
- Drawable icon = mContext.getDrawable(iconRes);
- final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
- ? (AnimatedVectorDrawable) icon
- : null;
- setImageDrawable(icon, false);
- if (mIsFaceUnlockState) {
- announceForAccessibility(getContext().getString(
- R.string.accessibility_scanning_face));
- }
-
- if (animation != null && isAnim) {
- animation.forceAnimationOnUI();
- animation.clearAnimationCallbacks();
- animation.registerAnimationCallback(new Animatable2.AnimationCallback() {
- @Override
- public void onAnimationEnd(Drawable drawable) {
- if (getDrawable() == animation && state == getState()
- && doesAnimationLoop(lockAnimIndex)) {
- animation.start();
- } else {
- Trace.endAsyncSection("LockIcon#Animation", state);
- }
- }
- });
- Trace.beginAsyncSection("LockIcon#Animation", state);
- animation.start();
- }
- }
- updateDarkTint();
-
- updateIconVisibility();
- updateClickability();
-
- return true;
+ mDrawableCache.clear();
}
/**
* Update the icon visibility
* @return true if the visibility changed
*/
- boolean updateIconVisibility() {
- boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked);
- boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning
- || mShowingLaunchAffordance;
- if (mBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) {
- if ((mHeadsUpManager.isHeadsUpGoingAway() || mHeadsUpManager.hasPinnedHeadsUp()
- || mStatusBarState == StatusBarState.KEYGUARD)
- && !mWakeUpCoordinator.getNotificationsFullyHidden()) {
- invisible = true;
- }
- }
- boolean wasInvisible = getVisibility() == INVISIBLE;
- if (invisible != wasInvisible) {
- setVisibility(invisible ? INVISIBLE : VISIBLE);
+ boolean updateIconVisibility(boolean visible) {
+ boolean wasVisible = getVisibility() == VISIBLE;
+ if (visible != wasVisible) {
+ setVisibility(visible ? VISIBLE : INVISIBLE);
animate().cancel();
- if (!invisible) {
+ if (visible) {
setScaleX(0);
setScaleY(0);
animate()
@@ -280,49 +135,47 @@
return false;
}
- private boolean canBlockUpdates() {
- return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway();
+ void update(int oldState, boolean pulsing, boolean dozing, boolean keyguardJustShown) {
+ mOldState = oldState;
+ mPulsing = pulsing;
+ mDozing = dozing;
+ mKeyguardJustShown = keyguardJustShown;
+
+ getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener);
}
- private void updateClickability() {
- if (mAccessibilityController == null) {
- return;
+ void setDozeAmount(float dozeAmount) {
+ mDozeAmount = dozeAmount;
+ updateDarkTint();
+ }
+
+ void onThemeChange(int iconColor) {
+ mDrawableCache.clear();
+ mIconColor = iconColor;
+ updateDarkTint();
+ }
+
+ private void updateDarkTint() {
+ int color = ColorUtils.blendARGB(mIconColor, Color.WHITE, mDozeAmount);
+ setImageTintList(ColorStateList.valueOf(color));
+ }
+
+ private Drawable getIcon(int newState) {
+ @LockAnimIndex final int lockAnimIndex =
+ getAnimationIndexForTransition(mOldState, newState, mPulsing, mDozing,
+ mKeyguardJustShown);
+
+ boolean isAnim = lockAnimIndex != -1;
+ int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(newState);
+
+ if (!mDrawableCache.contains(iconRes)) {
+ mDrawableCache.put(iconRes, getResources().getDrawable(iconRes));
}
- boolean canLock = mKeyguardStateController.isMethodSecure()
- && mKeyguardStateController.canDismissLockScreen();
- boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
- setClickable(clickToUnlock);
- setLongClickable(canLock && !clickToUnlock);
- setFocusable(mAccessibilityController.isAccessibilityEnabled());
+
+ return mDrawableCache.get(iconRes);
}
- @Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
- boolean fingerprintRunning = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
- // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
- // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
- // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
- // check of whether non-strong biometric is allowed
- boolean unlockingAllowed = mKeyguardUpdateMonitor
- .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */);
- if (fingerprintRunning && unlockingAllowed) {
- AccessibilityNodeInfo.AccessibilityAction unlock
- = new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfo.ACTION_CLICK,
- getContext().getString(R.string.accessibility_unlock_without_fingerprint));
- info.addAction(unlock);
- info.setHintText(getContext().getString(
- R.string.accessibility_waiting_for_fingerprint));
- } else if (mIsFaceUnlockState) {
- //Avoid 'button' to be spoken for scanning face
- info.setClassName(LockIcon.class.getName());
- info.setContentDescription(getContext().getString(
- R.string.accessibility_scanning_face));
- }
- }
-
- private int getIconForState(int state) {
+ static int getIconForState(int state) {
int iconRes;
switch (state) {
case STATE_LOCKED:
@@ -343,11 +196,7 @@
return iconRes;
}
- private boolean doesAnimationLoop(@LockAnimIndex int lockAnimIndex) {
- return lockAnimIndex == SCANNING;
- }
-
- private static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing,
+ static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing,
boolean dozing, boolean keyguardJustShown) {
// Never animate when screen is off
@@ -367,42 +216,10 @@
return -1;
}
- public void setBouncerShowingScrimmed(boolean bouncerShowing) {
- mBouncerShowingScrimmed = bouncerShowing;
- if (mBypassController.getBypassEnabled()) {
- update();
- }
- }
-
- /**
- * Animate padlock opening when bouncer challenge is solved.
- */
- public void onBouncerPreHideAnimation() {
- mBouncerPreHideAnimation = true;
- update();
- }
-
- void setIconColor(int iconColor) {
- mIconColor = iconColor;
- updateDarkTint();
- }
-
- void setSimLocked(boolean simLocked) {
- mSimLocked = simLocked;
- }
-
- /** Set if the device is docked. */
- public void setDocked(boolean docked) {
- if (mDocked != docked) {
- mDocked = docked;
- update();
- }
- }
-
@Retention(RetentionPolicy.SOURCE)
@IntDef({ERROR, UNLOCK, LOCK, SCANNING})
@interface LockAnimIndex {}
- private static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3;
+ static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3;
private static final int[][] LOCK_ANIM_RES_IDS = new int[][] {
{
R.anim.lock_to_error,
@@ -433,7 +250,7 @@
private int getThemedAnimationResId(@LockAnimIndex int lockAnimIndex) {
final String setting = TextUtils.emptyIfNull(
Settings.Secure.getString(getContext().getContentResolver(),
- Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES));
+ Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES));
if (setting.contains("com.android.theme.icon_pack.circular.android")) {
return LOCK_ANIM_RES_IDS[1][lockAnimIndex];
} else if (setting.contains("com.android.theme.icon_pack.filled.android")) {
@@ -444,83 +261,8 @@
return LOCK_ANIM_RES_IDS[0][lockAnimIndex];
}
- private int getState() {
- KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing
- || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) {
- return STATE_LOCK_OPEN;
- } else if (mTransientBiometricsError) {
- return STATE_BIOMETRICS_ERROR;
- } else if (updateMonitor.isFaceDetectionRunning() && !mPulsing) {
- return STATE_SCANNING_FACE;
- } else {
- return STATE_LOCKED;
- }
+ interface StateProvider {
+ int getState();
}
- /**
- * When keyguard is in pulsing (AOD2) state.
- * @param pulsing {@code true} when pulsing.
- */
- public void setPulsing(boolean pulsing) {
- mPulsing = pulsing;
- update();
- }
-
- private void updateDarkTint() {
- int color = ColorUtils.blendARGB(mIconColor, Color.WHITE, mDozeAmount);
- setImageTintList(ColorStateList.valueOf(color));
- }
-
- /**
- * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
- * icon on top of the black front scrim.
- * @param wakeAndUnlock are we wake and unlocking
- * @param isUnlock are we currently unlocking
- */
- public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) {
- if (wakeAndUnlock) {
- mWakeAndUnlockRunning = true;
- }
- if (isUnlock && mBypassController.getBypassEnabled() && canBlockUpdates()) {
- // We don't want the icon to change while we are unlocking
- mBlockUpdates = true;
- }
- update();
- }
-
- /**
- * When we're launching an affordance, like double pressing power to open camera.
- */
- public void onShowingLaunchAffordanceChanged(boolean showing) {
- mShowingLaunchAffordance = showing;
- update();
- }
-
- /**
- * Called whenever the scrims become opaque, transparent or semi-transparent.
- */
- public void onScrimVisibilityChanged(@ScrimVisibility int scrimsVisible) {
- if (mWakeAndUnlockRunning
- && scrimsVisible == ScrimController.TRANSPARENT) {
- mWakeAndUnlockRunning = false;
- update();
- }
- }
-
- void setDozing(boolean dozing) {
- mDozing = dozing;
- update();
- }
-
- void setDozeAmount(float dozeAmount) {
- mDozeAmount = dozeAmount;
- updateDarkTint();
- }
-
- /** Set the StatusBarState. */
- public void setStatusBarState(int statusBarState) {
- mStatusBarState = statusBarState;
- updateIconVisibility();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index 2b1a8a4..f7c861b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -16,11 +16,19 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_BIOMETRICS_ERROR;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCKED;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCK_OPEN;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_SCANNING_FACE;
+
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.hardware.biometrics.BiometricSourceType;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
import androidx.annotation.Nullable;
@@ -29,15 +37,18 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator.WakeUpListener;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.Optional;
@@ -59,6 +70,21 @@
private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
private final KeyguardBypassController mKeyguardBypassController;
private final Optional<DockManager> mDockManager;
+ private final KeyguardStateController mKeyguardStateController;
+ private final Resources mResources;
+ private final HeadsUpManagerPhone mHeadsUpManagerPhone;
+ private boolean mKeyguardShowing;
+ private boolean mKeyguardJustShown;
+ private boolean mBlockUpdates;
+ private boolean mPulsing;
+ private boolean mDozing;
+ private boolean mSimLocked;
+ private boolean mTransientBiometricsError;
+ private boolean mDocked;
+ private boolean mWakeAndUnlockRunning;
+ private boolean mShowingLaunchAffordance;
+ private boolean mBouncerShowingScrimmed;
+ private int mStatusBarState = StatusBarState.SHADE;
private LockIcon mLockIcon;
private View.OnAttachStateChangeListener mOnAttachStateChangeListener =
@@ -69,10 +95,13 @@
mConfigurationController.addCallback(mConfigurationListener);
mNotificationWakeUpCoordinator.addListener(mWakeUpListener);
mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+ mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
mDockManager.ifPresent(dockManager -> dockManager.addListener(mDockEventListener));
+ mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
mConfigurationListener.onThemeChanged();
+ update();
}
@Override
@@ -81,7 +110,7 @@
mConfigurationController.removeCallback(mConfigurationListener);
mNotificationWakeUpCoordinator.removeListener(mWakeUpListener);
mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
-
+ mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
mDockManager.ifPresent(dockManager -> dockManager.removeListener(mDockEventListener));
}
@@ -91,32 +120,44 @@
new StatusBarStateController.StateListener() {
@Override
public void onDozingChanged(boolean isDozing) {
- mLockIcon.setDozing(isDozing);
+ setDozing(isDozing);
}
@Override
public void onDozeAmountChanged(float linear, float eased) {
- mLockIcon.setDozeAmount(eased);
+ if (mLockIcon != null) {
+ mLockIcon.setDozeAmount(eased);
+ }
}
@Override
public void onStateChanged(int newState) {
- mLockIcon.setStatusBarState(newState);
+ setStatusBarState(newState);
}
};
private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+ private int mDensity;
+
@Override
public void onThemeChanged() {
+ if (mLockIcon == null) {
+ return;
+ }
+
TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes(
null, new int[]{ R.attr.wallpaperTextColor }, 0, 0);
int iconColor = typedArray.getColor(0, Color.WHITE);
typedArray.recycle();
- mLockIcon.setIconColor(iconColor);
+ mLockIcon.onThemeChange(iconColor);
}
@Override
public void onDensityOrFontScaleChanged() {
+ if (mLockIcon == null) {
+ return;
+ }
+
ViewGroup.LayoutParams lp = mLockIcon.getLayoutParams();
if (lp == null) {
return;
@@ -125,24 +166,41 @@
lp.height = mLockIcon.getResources().getDimensionPixelSize(
R.dimen.keyguard_lock_height);
mLockIcon.setLayoutParams(lp);
- mLockIcon.update(true /* force */);
+ update(true /* force */);
}
@Override
public void onLocaleListChanged() {
+ if (mLockIcon == null) {
+ return;
+ }
+
mLockIcon.setContentDescription(
mLockIcon.getResources().getText(R.string.accessibility_unlock_button));
- mLockIcon.update(true /* force */);
+ update(true /* force */);
+ }
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ final int density = newConfig.densityDpi;
+ if (density != mDensity) {
+ mDensity = density;
+ update();
+ }
}
};
private final WakeUpListener mWakeUpListener = new WakeUpListener() {
@Override
+ public void onPulseExpansionChanged(boolean expandingChanged) {
+ }
+
+ @Override
public void onFullyHiddenChanged(boolean isFullyHidden) {
if (mKeyguardBypassController.getBypassEnabled()) {
- boolean changed = mLockIcon.updateIconVisibility();
+ boolean changed = updateIconVisibility();
if (changed) {
- mLockIcon.update();
+ update();
}
}
}
@@ -152,30 +210,103 @@
new KeyguardUpdateMonitorCallback() {
@Override
public void onSimStateChanged(int subId, int slotId, int simState) {
- mLockIcon.setSimLocked(mKeyguardUpdateMonitor.isSimPinSecure());
- mLockIcon.update();
+ mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
+ update();
}
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
- mLockIcon.update();
+ update();
}
@Override
public void onBiometricRunningStateChanged(boolean running,
BiometricSourceType biometricSourceType) {
- mLockIcon.update();
+ update();
}
@Override
public void onStrongAuthStateChanged(int userId) {
- mLockIcon.update();
+ update();
}
};
private final DockManager.DockEventListener mDockEventListener =
- event -> mLockIcon.setDocked(event == DockManager.STATE_DOCKED
- || event == DockManager.STATE_DOCKED_HIDE);
+ event -> {
+ boolean docked =
+ event == DockManager.STATE_DOCKED || event == DockManager.STATE_DOCKED_HIDE;
+ if (docked != mDocked) {
+ mDocked = docked;
+ update();
+ }
+ };
+
+ private final KeyguardStateController.Callback mKeyguardMonitorCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ boolean force = false;
+ boolean wasShowing = mKeyguardShowing;
+ mKeyguardShowing = mKeyguardStateController.isShowing();
+ if (!wasShowing && mKeyguardShowing && mBlockUpdates) {
+ mBlockUpdates = false;
+ force = true;
+ }
+ if (!wasShowing && mKeyguardShowing) {
+ mKeyguardJustShown = true;
+ }
+ update(force);
+ }
+
+ @Override
+ public void onKeyguardFadingAwayChanged() {
+ if (!mKeyguardStateController.isKeyguardFadingAway()) {
+ if (mBlockUpdates) {
+ mBlockUpdates = false;
+ update(true /* force */);
+ }
+ }
+ }
+
+ @Override
+ public void onUnlockedChanged() {
+ update();
+ }
+ };
+
+ private final View.AccessibilityDelegate mAccessibilityDelegate =
+ new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ boolean fingerprintRunning =
+ mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
+ // Only checking if unlocking with Biometric is allowed (no matter strong or
+ // non-strong as long as primary auth, i.e. PIN/pattern/password, is not
+ // required), so it's ok to pass true for isStrongBiometric to
+ // isUnlockingWithBiometricAllowed() to bypass the check of whether non-strong
+ // biometric is allowed
+ boolean unlockingAllowed = mKeyguardUpdateMonitor
+ .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */);
+ if (fingerprintRunning && unlockingAllowed) {
+ AccessibilityNodeInfo.AccessibilityAction unlock =
+ new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK,
+ mResources.getString(
+ R.string.accessibility_unlock_without_fingerprint));
+ info.addAction(unlock);
+ info.setHintText(mResources.getString(
+ R.string.accessibility_waiting_for_fingerprint));
+ } else if (getState() == STATE_SCANNING_FACE) {
+ //Avoid 'button' to be spoken for scanning face
+ info.setClassName(LockIcon.class.getName());
+ info.setContentDescription(mResources.getString(
+ R.string.accessibility_scanning_face));
+ }
+ }
+ };
+ private int mLastState;
@Inject
public LockscreenLockIconController(LockscreenGestureLogger lockscreenGestureLogger,
@@ -188,7 +319,10 @@
ConfigurationController configurationController,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
KeyguardBypassController keyguardBypassController,
- @Nullable DockManager dockManager) {
+ @Nullable DockManager dockManager,
+ KeyguardStateController keyguardStateController,
+ @Main Resources resources,
+ HeadsUpManagerPhone headsUpManagerPhone) {
mLockscreenGestureLogger = lockscreenGestureLogger;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
@@ -200,24 +334,31 @@
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
mKeyguardBypassController = keyguardBypassController;
mDockManager = dockManager == null ? Optional.empty() : Optional.of(dockManager);
+ mKeyguardStateController = keyguardStateController;
+ mResources = resources;
+ mHeadsUpManagerPhone = headsUpManagerPhone;
mKeyguardIndicationController.setLockIconController(this);
}
/**
* Associate the controller with a {@link LockIcon}
+ *
+ * TODO: change to an init method and inject the view.
*/
public void attach(LockIcon lockIcon) {
mLockIcon = lockIcon;
mLockIcon.setOnClickListener(this::handleClick);
mLockIcon.setOnLongClickListener(this::handleLongClick);
+ mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate);
+ mLockIcon.setStateProvider(this::getState);
if (mLockIcon.isAttachedToWindow()) {
mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
}
mLockIcon.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
- mLockIcon.setStatusBarState(mStatusBarStateController.getState());
+ setStatusBarState(mStatusBarStateController.getState());
}
public LockIcon getView() {
@@ -228,8 +369,10 @@
* Called whenever the scrims become opaque, transparent or semi-transparent.
*/
public void onScrimVisibilityChanged(Integer scrimsVisible) {
- if (mLockIcon != null) {
- mLockIcon.onScrimVisibilityChanged(scrimsVisible);
+ if (mWakeAndUnlockRunning
+ && scrimsVisible == ScrimController.TRANSPARENT) {
+ mWakeAndUnlockRunning = false;
+ update();
}
}
@@ -237,55 +380,56 @@
* Propagate {@link StatusBar} pulsing state.
*/
public void setPulsing(boolean pulsing) {
- if (mLockIcon != null) {
- mLockIcon.setPulsing(pulsing);
- }
+ mPulsing = pulsing;
+ update();
}
/**
- * Called when the biometric authentication mode changes.
- *
- * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()}
- * @param isUnlock If the type is {@link BiometricUnlockController#isBiometricUnlock()} ()
+ * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
+ * icon on top of the black front scrim.
+ * @param wakeAndUnlock are we wake and unlocking
+ * @param isUnlock are we currently unlocking
*/
public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) {
- if (mLockIcon != null) {
- mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock, isUnlock);
+ if (wakeAndUnlock) {
+ mWakeAndUnlockRunning = true;
}
+ if (isUnlock && mKeyguardBypassController.getBypassEnabled() && canBlockUpdates()) {
+ // We don't want the icon to change while we are unlocking
+ mBlockUpdates = true;
+ }
+ update();
}
/**
* When we're launching an affordance, like double pressing power to open camera.
*/
public void onShowingLaunchAffordanceChanged(Boolean showing) {
- if (mLockIcon != null) {
- mLockIcon.onShowingLaunchAffordanceChanged(showing);
- }
+ mShowingLaunchAffordance = showing;
+ update();
}
/** Sets whether the bouncer is showing. */
public void setBouncerShowingScrimmed(boolean bouncerShowing) {
- if (mLockIcon != null) {
- mLockIcon.setBouncerShowingScrimmed(bouncerShowing);
+ mBouncerShowingScrimmed = bouncerShowing;
+ if (mKeyguardBypassController.getBypassEnabled()) {
+ update();
}
}
/**
- * When {@link KeyguardBouncer} starts to be dismissed and starts to play its animation.
+ * Animate padlock opening when bouncer challenge is solved.
*/
public void onBouncerPreHideAnimation() {
- if (mLockIcon != null) {
- mLockIcon.onBouncerPreHideAnimation();
- }
+ update();
}
/**
* If we're currently presenting an authentication error message.
*/
public void setTransientBiometricsError(boolean transientBiometricsError) {
- if (mLockIcon != null) {
- mLockIcon.setTransientBiometricsError(transientBiometricsError);
- }
+ mTransientBiometricsError = transientBiometricsError;
+ update();
}
private boolean handleLongClick(View view) {
@@ -306,4 +450,90 @@
}
mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
}
+
+ private void update() {
+ update(false /* force */);
+ }
+
+ private void update(boolean force) {
+ int state = getState();
+ boolean shouldUpdate = mLastState != state || force;
+ if (mBlockUpdates && canBlockUpdates()) {
+ shouldUpdate = false;
+ }
+ if (shouldUpdate && mLockIcon != null) {
+ mLockIcon.update(mLastState, mPulsing, mDozing, mKeyguardJustShown);
+ }
+ mLastState = state;
+ mKeyguardJustShown = false;
+ updateIconVisibility();
+ updateClickability();
+ }
+
+ private int getState() {
+ if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing
+ || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) {
+ return STATE_LOCK_OPEN;
+ } else if (mTransientBiometricsError) {
+ return STATE_BIOMETRICS_ERROR;
+ } else if (mKeyguardUpdateMonitor.isFaceDetectionRunning() && !mPulsing) {
+ return STATE_SCANNING_FACE;
+ } else {
+ return STATE_LOCKED;
+ }
+ }
+
+ private boolean canBlockUpdates() {
+ return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway();
+ }
+
+ private void setDozing(boolean isDozing) {
+ mDozing = isDozing;
+ update();
+ }
+
+ /** Set the StatusBarState. */
+ private void setStatusBarState(int statusBarState) {
+ mStatusBarState = statusBarState;
+ updateIconVisibility();
+ }
+
+ /**
+ * Update the icon visibility
+ * @return true if the visibility changed
+ */
+ private boolean updateIconVisibility() {
+ boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked);
+ boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning
+ || mShowingLaunchAffordance;
+ if (mKeyguardBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) {
+ if ((mHeadsUpManagerPhone.isHeadsUpGoingAway()
+ || mHeadsUpManagerPhone.hasPinnedHeadsUp()
+ || mStatusBarState == StatusBarState.KEYGUARD)
+ && !mNotificationWakeUpCoordinator.getNotificationsFullyHidden()) {
+ invisible = true;
+ }
+ }
+
+ if (mLockIcon == null) {
+ return false;
+ }
+
+ return mLockIcon.updateIconVisibility(!invisible);
+ }
+
+ private void updateClickability() {
+ if (mAccessibilityController == null) {
+ return;
+ }
+ boolean canLock = mKeyguardStateController.isMethodSecure()
+ && mKeyguardStateController.canDismissLockScreen();
+ boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
+ if (mLockIcon != null) {
+ mLockIcon.setClickable(clickToUnlock);
+ mLockIcon.setLongClickable(canLock && !clickToUnlock);
+ mLockIcon.setFocusable(mAccessibilityController.isAccessibilityEnabled());
+ }
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 56aae17..c637123 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -34,7 +34,6 @@
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.phone.LockIcon;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -148,11 +147,6 @@
KeyguardMessageArea createKeyguardMessageArea();
/**
- * Creates the keyguard LockIcon.
- */
- LockIcon createLockIcon();
-
- /**
* Creates the QSPanel.
*/
QSPanel createQSPanel();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 6e612d7..4f16031 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -62,7 +62,9 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
@@ -136,6 +138,9 @@
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
+ private SysUiState mSysUiState;
+ private boolean mSysUiStateBubblesExpanded;
+
@Captor
private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
@Captor
@@ -229,6 +234,11 @@
mZenModeConfig.suppressedVisualEffects = 0;
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
+ mSysUiState = new SysUiState();
+ mSysUiState.addCallback(sysUiFlags ->
+ mSysUiStateBubblesExpanded =
+ (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0);
+
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
mock(PowerManager.class),
@@ -257,7 +267,8 @@
mNotifPipeline,
mFeatureFlagsOldPipeline,
mDumpManager,
- mFloatingContentCoordinator);
+ mFloatingContentCoordinator,
+ mSysUiState);
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
mBubbleController.setExpandListener(mBubbleExpandListener);
@@ -277,6 +288,7 @@
assertTrue(mBubbleController.hasBubbles());
verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -284,6 +296,7 @@
assertFalse(mBubbleController.hasBubbles());
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -300,6 +313,8 @@
assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -323,6 +338,8 @@
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
eq(mRow.getEntry().getSbn()), anyInt());
assertFalse(mBubbleController.hasBubbles());
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -340,6 +357,8 @@
verify(mNotificationEntryManager, times(3)).updateNotifications(any());
assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
assertNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -363,6 +382,8 @@
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
assertTrue(mNotificationShadeWindowController.getBubbleExpanded());
+ assertTrue(mSysUiStateBubblesExpanded);
+
// Make sure the notif is suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
@@ -372,6 +393,8 @@
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
assertFalse(mBubbleController.isStackExpanded());
assertFalse(mNotificationShadeWindowController.getBubbleExpanded());
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -395,6 +418,8 @@
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
+ assertTrue(mSysUiStateBubblesExpanded);
+
// Last added is the one that is expanded
assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
@@ -416,6 +441,8 @@
// Collapse
mBubbleController.collapseStack();
assertFalse(mBubbleController.isStackExpanded());
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -437,6 +464,8 @@
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+ assertTrue(mSysUiStateBubblesExpanded);
+
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
@@ -463,6 +492,8 @@
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+ assertTrue(mSysUiStateBubblesExpanded);
+
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
@@ -493,6 +524,8 @@
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleController.expandStack();
+ assertTrue(mSysUiStateBubblesExpanded);
+
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
@@ -522,6 +555,8 @@
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
assertFalse(mBubbleController.hasBubbles());
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -541,6 +576,8 @@
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -559,6 +596,8 @@
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+ assertTrue(mSysUiStateBubblesExpanded);
}
@Test
@@ -579,6 +618,8 @@
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -605,6 +646,8 @@
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -619,6 +662,8 @@
mRow.getEntry().getKey(), mRow.getEntry(), REASON_APP_CANCEL);
mBubbleController.expandStackAndSelectBubble(key);
+
+ assertTrue(mSysUiStateBubblesExpanded);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 6244644..a31e3f8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -58,6 +58,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -132,6 +133,9 @@
private KeyguardBypassController mKeyguardBypassController;
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
+
+ private SysUiState mSysUiState = new SysUiState();
+
@Captor
private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
private TestableBubbleController mBubbleController;
@@ -242,7 +246,8 @@
mNotifPipeline,
mFeatureFlagsNewPipeline,
mDumpManager,
- mFloatingContentCoordinator);
+ mFloatingContentCoordinator,
+ mSysUiState);
mBubbleController.addNotifCallback(mNotifCallback);
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
mBubbleController.setExpandListener(mBubbleExpandListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index d3d90c4..f486102 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -19,6 +19,7 @@
import android.content.Context;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -52,12 +53,13 @@
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
DumpManager dumpManager,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState) {
super(context,
notificationShadeWindowController, statusBarStateController, shadeController,
data, Runnable::run, configurationController, interruptionStateProvider,
zenModeController, lockscreenUserManager, groupManager, entryManager,
- notifPipeline, featureFlags, dumpManager, floatingContentCoordinator);
+ notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState);
setInflateSynchronously(true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
index 487885a..85b5d70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
@@ -21,6 +21,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.res.Resources;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -35,6 +36,7 @@
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -71,6 +73,12 @@
private KeyguardBypassController mKeyguardBypassController;
@Mock
private DockManager mDockManager;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private HeadsUpManagerPhone mHeadsUpManagerPhone;
@Before
@@ -81,7 +89,8 @@
mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils,
mShadeController, mAccessibilityController, mKeyguardIndicationController,
mStatusBarStateController, mConfigurationController, mNotificationWakeUpCoordinator,
- mKeyguardBypassController, mDockManager);
+ mKeyguardBypassController, mDockManager, mKeyguardStateController, mResources,
+ mHeadsUpManagerPhone);
mLockIconController.attach(mLockIcon);
}
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 2fbba68..00d0d9c 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -60,6 +60,7 @@
hostdex: true, // for hiddenapi check
visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
apex_available: ["com.android.tethering"],
+ permitted_packages: ["android.net"],
}
stubs_defaults {
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
index 5de8171..4ba2c3d 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
@@ -199,7 +199,10 @@
return false;
}
- if (!mImeInputViewStarted || !autofillId.equalsIgnoreSession(mImeFieldId)) {
+ // TODO(b/151846600): IME doesn't have access to the virtual id of the webview, so we
+ // only compare the view id for now.
+ if (!mImeInputViewStarted || mImeFieldId == null
+ || autofillId.getViewId() != mImeFieldId.getViewId()) {
if (sDebug) {
Log.d(TAG,
"onInlineSuggestionsResponseLocked not sent because input view is not "
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ec84ae7..76a8e14 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -40,6 +40,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.uidRulesToString;
@@ -50,6 +51,7 @@
import static java.util.Map.Entry;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
@@ -2702,10 +2704,18 @@
switch (msg.what) {
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
- final NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
+ NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
if (networkCapabilities.hasConnectivityManagedCapability()) {
Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
}
+ if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+ // Make sure the original object is not mutated. NetworkAgent normally
+ // makes a copy of the capabilities when sending the message through
+ // the Messenger, but if this ever changes, not making a defensive copy
+ // here will give attack vectors to clients using this code path.
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ networkCapabilities.restrictCapabilitesForTestNetwork();
+ }
updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
break;
}
@@ -5778,7 +5788,16 @@
public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
- enforceNetworkFactoryPermission();
+ if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+ enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
+ // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
+ // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
+ // sees capabilities that may be malicious, which might prevent mistakes in the future.
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ networkCapabilities.restrictCapabilitesForTestNetwork();
+ } else {
+ enforceNetworkFactoryPermission();
+ }
LinkProperties lp = new LinkProperties(linkProperties);
lp.ensureDirectlyConnectedRoutes();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0bf81e0..bb94460 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -155,6 +155,7 @@
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.pm.Installer;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.storage.AppFuseBridge;
import com.android.server.storage.StorageSessionController;
import com.android.server.storage.StorageSessionController.ExternalStorageServiceException;
@@ -3268,6 +3269,25 @@
}
}
+ @Override
+ public void fixupAppDir(String path) {
+ final Matcher matcher = KNOWN_APP_DIR_PATHS.matcher(path);
+ if (matcher.matches()) {
+ AndroidPackage pkg = mPmInternal.getPackage(matcher.group(3));
+ if (pkg != null) {
+ try {
+ mVold.fixupAppDir(path + "/", pkg.getUid());
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.e(TAG, "Failed to fixup app dir for " + pkg.getPackageName(), e);
+ }
+ } else {
+ Log.e(TAG, "Can't find package belonging to " + path);
+ }
+ } else {
+ Log.e(TAG, "Path " + path + " is not a valid application-specific directory");
+ }
+ }
+
/** Not thread safe */
class AppFuseMountScope extends AppFuseBridge.MountScope {
private boolean mMounted = false;
diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
index 03f4a54..ba89fce 100644
--- a/services/core/java/com/android/server/am/BugReportHandlerUtil.java
+++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
@@ -16,20 +16,15 @@
package com.android.server.am;
-import static android.app.AppOpsManager.OP_NONE;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import android.app.Activity;
import android.app.BroadcastOptions;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
-import android.os.BugreportManager;
-import android.os.BugreportParams;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
@@ -115,17 +110,9 @@
options.setBackgroundActivityStartsAllowed(true);
final long identity = Binder.clearCallingIdentity();
try {
- // Handler app's BroadcastReceiver should call setResultCode(Activity.RESULT_OK) to
- // let ResultBroadcastReceiver know the handler app is available.
- context.sendOrderedBroadcastAsUser(intent,
- UserHandle.of(handlerUser),
+ context.sendBroadcastAsUser(intent, UserHandle.of(handlerUser),
android.Manifest.permission.DUMP,
- OP_NONE, options.toBundle(),
- new ResultBroadcastReceiver(),
- /* scheduler= */ null,
- Activity.RESULT_CANCELED,
- /* initialData= */ null,
- /* initialExtras= */ null);
+ options.toBundle());
} catch (RuntimeException e) {
Slog.e(TAG, "Error while trying to launch bugreport handler app.", e);
return false;
@@ -189,19 +176,4 @@
Binder.restoreCallingIdentity(identity);
}
}
-
- private static class ResultBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (getResultCode() == Activity.RESULT_OK) {
- return;
- }
-
- Slog.w(TAG, "Request bug report because handler app seems to be not available.");
- BugreportManager bugreportManager = context.getSystemService(BugreportManager.class);
- bugreportManager.requestBugreport(
- new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE),
- /* shareTitle= */null, /* shareDescription= */ null);
- }
- }
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 58e332a..c1fbcfb 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -653,9 +653,6 @@
mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
- mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
- GnssLocationProvider.this::onNetworkAvailable, mLooper);
-
// App ops service to keep track of who is accessing the GPS
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -677,6 +674,9 @@
mNIHandler = new GpsNetInitiatedHandler(context,
mNetInitiatedListener,
mSuplEsEnabled);
+ mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
+ GnssLocationProvider.this::onNetworkAvailable, mLooper, mNIHandler);
+
sendMessage(INITIALIZE_HANDLER, 0, null);
mGnssStatusListenerHelper = new GnssStatusListenerHelper(mContext, mHandler) {
diff --git a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
index 93227bd..5d6474b 100644
--- a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
@@ -29,12 +29,22 @@
import android.provider.Telephony.Carriers;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.PreciseCallState;
+import android.telephony.PhoneStateListener;
import android.util.Log;
+import com.android.internal.location.GpsNetInitiatedHandler;
+
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
+import java.util.Map;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Iterator;
/**
* Handles network connection requests and network state change updates for AGPS data download.
@@ -86,6 +96,9 @@
private HashMap<Network, NetworkAttributes> mAvailableNetworkAttributes =
new HashMap<>(HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS);
+ // Phone State Listeners to track all the active sub IDs
+ private HashMap<Integer, SubIdPhoneStateListener> mPhoneStateListeners;
+
private final ConnectivityManager mConnMgr;
private final Handler mHandler;
@@ -94,6 +107,9 @@
private int mAGpsDataConnectionState;
private InetAddress mAGpsDataConnectionIpAddr;
private int mAGpsType;
+ private int mActiveSubId = -1;
+ private final GpsNetInitiatedHandler mNiHandler;
+
private final Context mContext;
@@ -166,18 +182,109 @@
GnssNetworkConnectivityHandler(Context context,
GnssNetworkListener gnssNetworkListener,
- Looper looper) {
+ Looper looper,
+ GpsNetInitiatedHandler niHandler) {
mContext = context;
mGnssNetworkListener = gnssNetworkListener;
+ SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
+ if (subManager != null) {
+ subManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener);
+ }
+
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
mHandler = new Handler(looper);
+ mNiHandler = niHandler;
mConnMgr = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
mSuplConnectivityCallback = createSuplConnectivityCallback();
}
+ /**
+ * SubId Phone State Listener is used cache the last active Sub ID when a call is made,
+ * which will be used during an emergency call to set the Network Specifier to the particular
+ * sub when an emergency supl connection is requested
+ */
+ private final class SubIdPhoneStateListener extends PhoneStateListener {
+ private Integer mSubId;
+ SubIdPhoneStateListener(Integer subId) {
+ mSubId = subId;
+ }
+ @Override
+ public void onPreciseCallStateChanged(PreciseCallState state) {
+ if (state.PRECISE_CALL_STATE_ACTIVE == state.getForegroundCallState()) {
+ mActiveSubId = mSubId;
+ if (DEBUG) Log.d(TAG, "mActiveSubId: " + mActiveSubId);
+ }
+ }
+ };
+
+ /**
+ * Subscription Changed Listener is used to get all active subscriptions and create a
+ * Phone State Listener for each Sub ID that we find in the active subscription list
+ */
+ private final SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangeListener
+ = new SubscriptionManager.OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ if (mPhoneStateListeners == null) {
+ // Capacity=2 Load-Factor=1.0, as typically no more than 2 SIMs
+ mPhoneStateListeners = new HashMap<Integer, SubIdPhoneStateListener>(2,1);
+ }
+ SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
+ TelephonyManager telManager = mContext.getSystemService(TelephonyManager.class);
+ if (subManager != null && telManager != null) {
+ List<SubscriptionInfo> subscriptionInfoList =
+ subManager.getActiveSubscriptionInfoList();
+ HashSet<Integer> activeSubIds = new HashSet<Integer>();
+ if (subscriptionInfoList != null) {
+ if (DEBUG) Log.d(TAG, "Active Sub List size: " + subscriptionInfoList.size());
+ // populate phone state listeners with all new active subs
+ for (SubscriptionInfo subInfo : subscriptionInfoList) {
+ activeSubIds.add(subInfo.getSubscriptionId());
+ if (!mPhoneStateListeners.containsKey(subInfo.getSubscriptionId())) {
+ TelephonyManager subIdTelManager =
+ telManager.createForSubscriptionId(subInfo.getSubscriptionId());
+ if (subIdTelManager != null) {
+ if (DEBUG) Log.d(TAG, "Listener sub" + subInfo.getSubscriptionId());
+ SubIdPhoneStateListener subIdPhoneStateListener =
+ new SubIdPhoneStateListener(subInfo.getSubscriptionId());
+ mPhoneStateListeners.put(subInfo.getSubscriptionId(),
+ subIdPhoneStateListener);
+ subIdTelManager.listen(subIdPhoneStateListener,
+ PhoneStateListener.LISTEN_PRECISE_CALL_STATE);
+ }
+ }
+ }
+ }
+ // clean up phone state listeners than no longer have active subs
+ Iterator<Map.Entry<Integer, SubIdPhoneStateListener> > iterator =
+ mPhoneStateListeners.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<Integer, SubIdPhoneStateListener> element = iterator.next();
+ if (!activeSubIds.contains(element.getKey())) {
+ TelephonyManager subIdTelManager =
+ telManager.createForSubscriptionId(element.getKey());
+ if (subIdTelManager != null) {
+ if (DEBUG) Log.d(TAG, "unregister listener sub " + element.getKey());
+ subIdTelManager.listen(element.getValue(),
+ PhoneStateListener.LISTEN_NONE);
+ // removes the element from mPhoneStateListeners
+ iterator.remove();
+ } else {
+ Log.e(TAG, "Telephony Manager for Sub " + element.getKey() + " null");
+ }
+ }
+ }
+ // clean up cached active phone call sub if it is no longer an active sub
+ if (!activeSubIds.contains(mActiveSubId)) {
+ mActiveSubId = -1;
+ }
+ }
+ }
+ };
+
void registerNetworkCallbacks() {
// register for connectivity change events.
NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
@@ -467,6 +574,12 @@
NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
networkRequestBuilder.addCapability(getNetworkCapability(mAGpsType));
networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ // During an emergency call, and when we have cached the Active Sub Id, we set the
+ // Network Specifier so that the network request goes to the correct Sub Id
+ if (mNiHandler.getInEmergency() && mActiveSubId >= 0) {
+ if (DEBUG) Log.d(TAG, "Adding Network Specifier: " + Integer.toString(mActiveSubId));
+ networkRequestBuilder.setNetworkSpecifier(Integer.toString(mActiveSubId));
+ }
NetworkRequest networkRequest = networkRequestBuilder.build();
mConnMgr.requestNetwork(
networkRequest,
@@ -598,6 +711,15 @@
}
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ // During an emergency call with an active sub id, get the Telephony Manager specific
+ // to the active sub to get the correct value from getServiceState and getNetworkType
+ if (mNiHandler.getInEmergency() && mActiveSubId >= 0) {
+ TelephonyManager subIdTelManager =
+ phone.createForSubscriptionId(mActiveSubId);
+ if (subIdTelManager != null) {
+ phone = subIdTelManager;
+ }
+ }
ServiceState serviceState = phone.getServiceState();
String projection = null;
String selection = null;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 60c3875..7f805be 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -139,6 +139,7 @@
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -387,10 +388,9 @@
* still post toasts created with
* {@link android.widget.Toast#makeText(Context, CharSequence, int)} and its variants while
* in the background.
- *
- * TODO(b/144152069): Add @EnabledAfter(Q) to target R+ after assessing impact on dogfood
*/
@ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private static final long CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK = 128611929L;
private IActivityManager mAm;
@@ -2751,24 +2751,18 @@
@Override
public void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration,
int displayId, @Nullable ITransientNotificationCallback callback) {
- enqueueToast(pkg, token, text, null, duration, displayId, callback, false);
+ enqueueToast(pkg, token, text, null, duration, displayId, callback);
}
@Override
public void enqueueToast(String pkg, IBinder token, ITransientNotification callback,
int duration, int displayId) {
- enqueueToast(pkg, token, null, callback, duration, displayId, null, true);
- }
-
- @Override
- public void enqueueTextOrCustomToast(String pkg, IBinder token,
- ITransientNotification callback, int duration, int displayId, boolean isCustom) {
- enqueueToast(pkg, token, null, callback, duration, displayId, null, isCustom);
+ enqueueToast(pkg, token, null, callback, duration, displayId, null);
}
private void enqueueToast(String pkg, IBinder token, @Nullable CharSequence text,
@Nullable ITransientNotification callback, int duration, int displayId,
- @Nullable ITransientNotificationCallback textCallback, boolean isCustom) {
+ @Nullable ITransientNotificationCallback textCallback) {
if (DBG) {
Slog.i(TAG, "enqueueToast pkg=" + pkg + " token=" + token
+ " duration=" + duration + " displayId=" + displayId);
@@ -2807,11 +2801,15 @@
}
boolean isAppRenderedToast = (callback != null);
- if (isAppRenderedToast && isCustom && !isSystemToast
- && !isPackageInForegroundForToast(pkg, callingUid)) {
+ if (isAppRenderedToast && !isSystemToast && !isPackageInForegroundForToast(pkg,
+ callingUid)) {
boolean block;
long id = Binder.clearCallingIdentity();
try {
+ // CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK is gated on targetSdk, so block will be
+ // false for apps with targetSdk < R. For apps with targetSdk R+, text toasts
+ // are not app-rendered, so isAppRenderedToast == true means it's a custom
+ // toast.
block = mPlatformCompat.isChangeEnabledByPackageName(
CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK, pkg,
callingUser.getIdentifier());
@@ -2824,11 +2822,6 @@
Binder.restoreCallingIdentity(id);
}
if (block) {
- // TODO(b/144152069): Remove informative toast
- mUiHandler.post(() -> Toast.makeText(getContext(),
- "Background custom toast blocked for package " + pkg + ".\n"
- + "See g.co/dev/toast.",
- Toast.LENGTH_SHORT).show());
Slog.w(TAG, "Blocking custom toast from package " + pkg
+ " due to package not in the foreground");
return;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4e5d6c4..e605eeb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5070,15 +5070,16 @@
* action and a {@code android.intent.category.BROWSABLE} category</li>
* </ul>
*/
- int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps) {
+ int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
+ boolean matchSystemOnly) {
return updateFlagsForResolve(flags, userId, callingUid,
- wantInstantApps, false /*onlyExposedExplicitly*/);
+ wantInstantApps, matchSystemOnly, false /*onlyExposedExplicitly*/);
}
int updateFlagsForResolve(int flags, int userId, int callingUid,
- boolean wantInstantApps, boolean onlyExposedExplicitly) {
+ boolean wantInstantApps, boolean onlyExposedExplicitly, boolean matchSystemOnly) {
// Safe mode means we shouldn't match any third-party components
- if (mSafeMode) {
+ if (mSafeMode || matchSystemOnly) {
flags |= PackageManager.MATCH_SYSTEM_ONLY;
}
if (getInstantAppPackageName(callingUid) != null) {
@@ -6170,7 +6171,8 @@
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart);
+ flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
@@ -6202,7 +6204,8 @@
intent = updateIntentForResolve(intent);
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
final int flags = updateFlagsForResolve(
- 0, userId, callingUid, false /*includeInstantApps*/);
+ 0, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
synchronized (mLock) {
@@ -6523,7 +6526,8 @@
android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
flags = updateFlagsForResolve(
- flags, userId, callingUid, false /*includeInstantApps*/);
+ flags, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
intent = updateIntentForResolve(intent);
// writer
synchronized (mLock) {
@@ -6735,7 +6739,8 @@
}
synchronized (mLock) {
int flags = updateFlagsForResolve(0, parent.id, callingUid,
- false /*includeInstantApps*/);
+ false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr(
intent, resolvedType, flags, sourceUserId, parent.id);
return xpDomainInfo != null;
@@ -6821,7 +6826,8 @@
}
flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
- comp != null || pkgName != null /*onlyExposedExplicitly*/);
+ comp != null || pkgName != null /*onlyExposedExplicitly*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<>(1);
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
@@ -7606,7 +7612,8 @@
String resolvedType, int flags, int userId) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent activity options");
@@ -7792,7 +7799,8 @@
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -7882,7 +7890,8 @@
private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,
int userId, int callingUid) {
if (!mUserManager.exists(userId)) return null;
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ false /* matchSystemOnly */);
List<ResolveInfo> query = queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
if (query != null) {
@@ -7913,7 +7922,8 @@
false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps);
+ flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
+ false /* matchSystemOnly */);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -8050,7 +8060,8 @@
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ false /* matchSystemOnly */);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index acf3f79..74958b6 100644
--- a/services/core/java/com/android/server/power/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -3,6 +3,7 @@
{
"name": "CtsBatterySavingTestCases",
"options": [
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.LargeTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
@@ -11,6 +12,7 @@
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.power"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -18,6 +20,7 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.power"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{
"exclude-filter": "com.android.server.power.PowerManagerServiceTest#testWakefulnessAwake_ShouldWakeUpWhenPluggedIn"
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index fb22481..c6f375e 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -302,9 +302,6 @@
private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
- // TODO(task-hierarchy): remove when tiles can be actual parents
- TaskTile mTile = null;
-
private final Handler mHandler;
private class ActivityStackHandler extends Handler {
@@ -550,10 +547,10 @@
}
ActivityStack(ActivityTaskManagerService atmService, int id, int activityType,
- ActivityInfo info, Intent intent) {
+ ActivityInfo info, Intent intent, boolean createdByOrganizer) {
this(atmService, id, info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
null /*taskDescription*/, null /*stack*/);
-
+ mCreatedByOrganizer = createdByOrganizer;
setActivityType(activityType);
}
@@ -600,20 +597,11 @@
}
@Override
- public void resolveTileOverrideConfiguration(Configuration newParentConfig) {
- super.resolveTileOverrideConfiguration(newParentConfig);
- if (mTile != null) {
- // If this is a virtual child of a tile, simulate the parent-child relationship
- mTile.updateResolvedConfig(getResolvedOverrideConfiguration());
- }
- }
-
- @Override
public void onConfigurationChanged(Configuration newParentConfig) {
// Calling Task#onConfigurationChanged() for leaf task since the ops in this method are
// particularly for ActivityStack, like preventing bounds changes when inheriting certain
// windowing mode.
- if (!isRootTask() || this instanceof TaskTile) {
+ if (!isRootTask()) {
super.onConfigurationChanged(newParentConfig);
return;
}
@@ -688,6 +676,9 @@
@Override
public void setWindowingMode(int windowingMode) {
+ // Reset the cached result of toString()
+ stringName = null;
+
// Calling Task#setWindowingMode() for leaf task since this is the a specialization of
// {@link #setWindowingMode(int)} for ActivityStack.
if (!isRootTask()) {
@@ -741,7 +732,6 @@
final int currentOverrideMode = getRequestedOverrideWindowingMode();
final DisplayContent display = getDisplay();
final Task topTask = getTopMostTask();
- final ActivityStack splitScreenStack = display.getRootSplitScreenPrimaryTask();
int windowingMode = preferredWindowingMode;
if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED
&& isTransientWindowingMode(currentMode)) {
@@ -755,14 +745,14 @@
windowingMode = display.validateWindowingMode(windowingMode,
null /* ActivityRecord */, topTask, getActivityType());
}
- if (splitScreenStack == this
+ if (display.getRootSplitScreenPrimaryTask() == this
&& windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
// Resolution to split-screen secondary for the primary split-screen stack means
// we want to leave split-screen mode.
windowingMode = mRestoreOverrideWindowingMode;
}
- final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryTask();
+ final boolean alreadyInSplitScreenMode = display.isSplitScreenModeActivated();
// Don't send non-resizeable notifications if the windowing mode changed was a side effect
// of us entering split-screen mode.
@@ -830,7 +820,7 @@
return;
}
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && splitScreenStack != null) {
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && alreadyInSplitScreenMode) {
// We already have a split-screen stack in this display, so just move the tasks over.
// TODO: Figure-out how to do all the stuff in
// AMS.setTaskWindowingModeSplitScreenPrimary
@@ -1062,7 +1052,7 @@
final DisplayContent display = getDisplay();
if (inSplitScreenSecondaryWindowingMode()) {
- // If the stack is in split-screen seconardy mode, we need to make sure we move the
+ // If the stack is in split-screen secondary mode, we need to make sure we move the
// primary split-screen stack forward in the case it is currently behind a fullscreen
// stack so both halves of the split-screen appear on-top and the fullscreen stack isn't
// cutting between them.
@@ -1084,12 +1074,13 @@
display.moveHomeStackToFront(reason + " returnToHome");
}
- final boolean movingTask = task != null;
- display.positionStackAtTop(this, !movingTask /* includingParents */, reason);
- if (movingTask) {
- // This also moves the entire hierarchy branch to top, including parents
- positionChildAtTop(task);
+ if (isRootTask()) {
+ display.positionStackAtTop(this, false /* includingParents */, reason);
}
+ if (task == null) {
+ task = this;
+ }
+ task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);
}
/**
@@ -1115,12 +1106,6 @@
}
}
- @Override
- boolean isFocusable() {
- // Special check for tile which isn't really in the hierarchy
- return mTile != null ? mTile.isFocusable() : super.isFocusable();
- }
-
boolean isTopActivityFocusable() {
final ActivityRecord r = topRunningActivity();
return r != null ? r.isFocusable()
@@ -3541,6 +3526,10 @@
@Override
void onChildPositionChanged(WindowContainer child) {
+ if (isOrganized()) {
+ mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */);
+ }
+
if (!mChildren.contains(child)) {
return;
}
@@ -3571,11 +3560,6 @@
if (oldDisplay != null && oldDisplay.isRemoving()) {
postReparent();
}
- if (mTile != null && getSurfaceControl() != null) {
- // by now, the TaskStack should already have been reparented, so we can reparent its
- // surface here
- reparentSurfaceControl(getPendingTransaction(), mTile.getSurfaceControl());
- }
}
void reparent(DisplayContent newParent, boolean onTop) {
@@ -3613,16 +3597,7 @@
@Override
void getRelativeDisplayedPosition(Point outPos) {
- // check for tile which is "virtually" a parent.
- if (mTile != null) {
- final Rect dispBounds = getDisplayedBounds();
- outPos.set(dispBounds.left, dispBounds.top);
- final Rect parentBounds = mTile.getBounds();
- outPos.offset(-parentBounds.left, -parentBounds.top);
- } else {
- super.getRelativeDisplayedPosition(outPos);
- }
-
+ super.getRelativeDisplayedPosition(outPos);
final int outset = getStackOutset();
outPos.x -= outset;
outPos.y -= outset;
@@ -3632,16 +3607,6 @@
if (mSurfaceControl == null) {
return;
}
- if (mTile != null) {
- // Tile controls crop, so the app needs to be able to draw its background outside of
- // the stack bounds for when the tile crop gets bigger than the stack.
- if (mLastSurfaceSize.equals(0, 0)) {
- return;
- }
- transaction.setWindowCrop(mSurfaceControl, null);
- mLastSurfaceSize.set(0, 0);
- return;
- }
final Rect stackBounds = getDisplayedBounds();
int width = stackBounds.width();
@@ -3665,9 +3630,6 @@
@Override
void onDisplayChanged(DisplayContent dc) {
- if (mTile != null && dc != mTile.getDisplay()) {
- mTile.removeChild(this);
- }
super.onDisplayChanged(dc);
if (isRootTask()) {
updateSurfaceBounds();
@@ -3810,49 +3772,6 @@
return shouldSleepActivities() || mAtmService.mShuttingDown;
}
- TaskTile getTile() {
- return mTile;
- }
-
- /**
- * Don't call this directly. instead use {@link TaskTile#addChild} or
- * {@link TaskTile#removeChild}.
- */
- void setTile(TaskTile tile) {
- TaskTile origTile = mTile;
- mTile = tile;
- final ConfigurationContainer parent = getParent();
- if (parent != null) {
- onConfigurationChanged(parent.getConfiguration());
- }
-
- // Reparent to tile surface or back to original parent
- if (getSurfaceControl() == null) {
- return;
- }
- if (mTile != null) {
- // don't use reparentSurfaceControl because we need to bypass taskorg check
- mSurfaceAnimator.reparent(getPendingTransaction(), mTile.getSurfaceControl());
- } else if (mTile == null && origTile != null) {
- mSurfaceAnimator.reparent(getPendingTransaction(), getParentSurfaceControl());
- }
- }
-
- @Override
- public SurfaceControl getParentSurfaceControl() {
- // Tile is a "virtual" parent, so we need to intercept the parent surface here
- return mTile != null ? mTile.getSurfaceControl() : super.getParentSurfaceControl();
- }
-
- @Override
- void removeImmediately() {
- // TODO(task-hierarchy): remove this override when tiles are in hierarchy
- if (mTile != null) {
- mTile.removeChild(this);
- }
- super.removeImmediately();
- }
-
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
@WindowTraceLogLevel int logLevel) {
@@ -3897,7 +3816,7 @@
proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
}
- proto.write(CREATED_BY_ORGANIZER, this instanceof TaskTile);
+ proto.write(CREATED_BY_ORGANIZER, mCreatedByOrganizer);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 57f357d..4652f49 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -423,6 +423,10 @@
final ActivityStack toStack = mToDisplay.getOrCreateStack(
null, mTmpOptions, task, task.getActivityType(), mOnTop);
+ if (task == toStack) {
+ // The task was reused as the root task.
+ return;
+ }
if (mOnTop) {
final boolean isTopTask = task == mTopTask;
@@ -1704,7 +1708,7 @@
mRootWindowContainer.getLaunchStack(null, aOptions, task, onTop);
final WindowContainer parent = task.getParent();
- if (parent == stack) {
+ if (parent == stack || task == stack) {
// Nothing else to do since it is already restored in the right stack.
return true;
}
@@ -2237,7 +2241,7 @@
final boolean isSecondaryDisplayPreferred =
(preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY);
final boolean inSplitScreenMode = actualStack != null
- && actualStack.getDisplay().hasSplitScreenPrimaryTask();
+ && actualStack.getDisplay().isSplitScreenModeActivated();
if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
&& !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) {
return;
@@ -2284,16 +2288,14 @@
if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
// we need to move it to top of fullscreen stack, otherwise it will be covered.
-
- final ActivityStack dockedStack =
- task.getStack().getDisplay().getRootSplitScreenPrimaryTask();
- if (dockedStack != null) {
+ final DisplayContent display = task.getStack().getDisplay();
+ if (display.isSplitScreenModeActivated()) {
// Display a warning toast that we tried to put an app that doesn't support
// split-screen in split-screen.
mService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedStack();
- dockedStack.getDisplay().onSplitScreenModeDismissed();
- dockedStack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+ display.onSplitScreenModeDismissed();
+ display.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
true /* notifyClients */);
}
return;
@@ -2602,7 +2604,7 @@
"startActivityFromRecents: Task " + taskId + " not found.");
} else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
&& task.getWindowingMode() != windowingMode) {
- mService.moveTaskToSplitScreenPrimaryTile(task, true /* toTop */);
+ mService.moveTaskToSplitScreenPrimaryTask(task, true /* toTop */);
}
if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 14ca7cb..da1c045 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -30,7 +30,6 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WaitResult.LAUNCH_STATE_COLD;
import static android.app.WaitResult.LAUNCH_STATE_HOT;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -78,6 +77,7 @@
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1566,7 +1566,7 @@
}
if (!mAvoidMoveToFront && mDoResume) {
- mTargetStack.moveToFront("reuseOrNewTask");
+ mTargetStack.getStack().moveToFront("reuseOrNewTask", targetTask);
if (mOptions != null) {
if (mPreferredWindowingMode != WINDOWING_MODE_UNDEFINED) {
mTargetStack.setWindowingMode(mPreferredWindowingMode);
@@ -2364,6 +2364,7 @@
private void setTargetStackIfNeeded(ActivityRecord intentActivity) {
mTargetStack = intentActivity.getRootTask();
mTargetStack.mLastPausedActivity = null;
+ Task intentTask = intentActivity.getTask();
// If the target task is not in the front, then we need to bring it to the front...
// except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
// the same behavior as if a new instance was being started, which means not bringing it
@@ -2374,7 +2375,7 @@
final ActivityRecord curTop = (focusStack == null)
? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
final Task topTask = curTop != null ? curTop.getTask() : null;
- differentTopTask = topTask != intentActivity.getTask()
+ differentTopTask = topTask != intentTask
|| (focusStack != null && topTask != focusStack.getTopMostTask());
} else {
// The existing task should always be different from those in other displays.
@@ -2391,7 +2392,6 @@
intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
}
- final Task intentTask = intentActivity.getTask();
final ActivityStack launchStack =
getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions);
if (launchStack == null || launchStack == mTargetStack) {
@@ -2400,6 +2400,14 @@
// new intent has delivered.
final boolean isSplitScreenTopStack = mTargetStack.isTopSplitScreenStack();
+ // TODO(b/151572268): Figure out a better way to move tasks in above 2-levels
+ // tasks hierarchies.
+ if (mTargetStack != intentTask
+ && mTargetStack != intentTask.getParent().asTask()) {
+ intentTask.getParent().positionChildAt(POSITION_TOP, intentTask,
+ false /* includingParents */);
+ intentTask = intentTask.getParent().asTask();
+ }
// We only want to move to the front, if we aren't going to launch on a
// different stack. If we launch on a different stack, we will put the
// task on top there.
@@ -2420,8 +2428,8 @@
// Need to update mTargetStack because if task was moved out of it, the original stack may
// be destroyed.
mTargetStack = intentActivity.getRootTask();
- mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(),
- WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack);
+ mSupervisor.handleNonResizableTaskIfNeeded(intentTask, WINDOWING_MODE_UNDEFINED,
+ DEFAULT_DISPLAY, mTargetStack);
}
private void resumeTargetStackIfNeeded() {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 214a676..a5b0026 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -31,12 +31,12 @@
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -122,6 +122,7 @@
import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.Manifest;
import android.annotation.IntDef;
@@ -144,7 +145,6 @@
import android.app.IAssistDataReceiver;
import android.app.INotificationManager;
import android.app.IRequestFinishCallback;
-import android.window.ITaskOrganizerController;
import android.app.ITaskStackListener;
import android.app.Notification;
import android.app.NotificationManager;
@@ -230,9 +230,9 @@
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
+import android.view.WindowManager;
import android.window.IWindowOrganizerController;
import android.window.WindowContainerTransaction;
-import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -1274,9 +1274,13 @@
a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchActivityType(ACTIVITY_TYPE_DREAM);
+
try {
getActivityStartController().obtainStarter(intent, "dream")
.setActivityInfo(a)
+ .setActivityOptions(options.toBundle())
.setIsDream(true)
.execute();
return true;
@@ -2349,16 +2353,18 @@
}
final ActivityStack stack = task.getStack();
- // Convert some windowing-mode changes into root-task reparents for split-screen.
- if (stack.getTile() != null) {
- stack.getDisplay().onSplitScreenModeDismissed();
- }
if (toTop) {
stack.moveToFront("setTaskWindowingMode", task);
}
- stack.setWindowingMode(windowingMode);
- stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
- true /* notifyClients */);
+ // Convert some windowing-mode changes into root-task reparents for split-screen.
+ if (stack.inSplitScreenWindowingMode()) {
+ stack.getDisplay().onSplitScreenModeDismissed();
+
+ } else {
+ stack.setWindowingMode(windowingMode);
+ stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+ true /* notifyClients */);
+ }
return true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2755,24 +2761,22 @@
}
final int prevMode = task.getWindowingMode();
- moveTaskToSplitScreenPrimaryTile(task, toTop);
+ moveTaskToSplitScreenPrimaryTask(task, toTop);
return prevMode != task.getWindowingMode();
}
- void moveTaskToSplitScreenPrimaryTile(Task task, boolean toTop) {
- ActivityStack stack = task.getStack();
- TaskTile tile = null;
- for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) {
- tile = stack.getDisplay().getStackAt(i).asTile();
- if (tile != null && tile.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- break;
- }
+ void moveTaskToSplitScreenPrimaryTask(Task task, boolean toTop) {
+ final DisplayContent display = task.getDisplayContent();
+ final ActivityStack primarySplitTask = display.getRootSplitScreenPrimaryTask();
+ if (primarySplitTask == null) {
+ throw new IllegalStateException("Can't enter split without associated organized task");
}
- if (tile == null) {
- throw new IllegalStateException("Can't enter split without associated tile");
+
+ if (toTop) {
+ display.positionStackAt(POSITION_TOP, primarySplitTask, false /* includingParents */);
}
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop);
+ wct.reparent(task.getStack().mRemoteToken, primarySplitTask.mRemoteToken, toTop);
mWindowOrganizerController.applyTransaction(wct);
}
@@ -3239,7 +3243,8 @@
final ActivityStack stack = r.getRootTask();
final Task task = stack.getDisplay().createStack(stack.getWindowingMode(),
- stack.getActivityType(), !ON_TOP, ainfo, intent);
+ stack.getActivityType(), !ON_TOP, ainfo, intent,
+ false /* createdByOrganizer */);
if (!mRecentTasks.addToBottom(task)) {
// The app has too many tasks already and we can't add any more
@@ -4278,19 +4283,9 @@
try {
synchronized (mGlobalLock) {
final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
- TaskTile primary = null;
- TaskTile secondary = null;
- for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final TaskTile t = dc.getStackAt(i).asTile();
- if (t == null) {
- continue;
- }
- if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- primary = t;
- } else if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- secondary = t;
- }
- }
+ final Task primary = dc.getRootSplitScreenPrimaryTask();
+ final Task secondary = dc.getTask(t -> t.mCreatedByOrganizer && t.isRootTask()
+ && t.inSplitScreenSecondaryWindowingMode());
if (primary == null || secondary == null) {
return;
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 33dd9cf..1036af6 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -467,6 +468,10 @@
return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
}
+ public boolean isActivityTypeDream() {
+ return getActivityType() == ACTIVITY_TYPE_DREAM;
+ }
+
public boolean isActivityTypeStandard() {
return getActivityType() == ACTIVITY_TYPE_STANDARD;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7df731b..ede4569 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -200,7 +200,6 @@
import android.view.Gravity;
import android.view.IDisplayWindowInsetsController;
import android.view.ISystemGestureExclusionListener;
-import android.window.ITaskOrganizer;
import android.view.IWindow;
import android.view.InputChannel;
import android.view.InputDevice;
@@ -217,6 +216,7 @@
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
+import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -456,6 +456,8 @@
private final Configuration mTmpConfiguration = new Configuration();
+ private ArrayList<Task> mTmpTasks = new ArrayList<>();
+
/** Remove this display when animation on it has completed. */
private boolean mDeferredRemoval;
@@ -654,8 +656,8 @@
private final RootWindowContainer.FindTaskResult
mTmpFindTaskResult = new RootWindowContainer.FindTaskResult();
- // When non-null, new stacks get put into this tile.
- TaskTile mLaunchTile = null;
+ // When non-null, new tasks get put into this root task.
+ Task mLaunchRootTask = null;
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
@@ -2128,12 +2130,13 @@
}
/** @return The primary split-screen task, and {@code null} otherwise. */
- ActivityStack getRootSplitScreenPrimaryTask() {
+ @Nullable ActivityStack getRootSplitScreenPrimaryTask() {
return mTaskContainers.getRootSplitScreenPrimaryTask();
}
- boolean hasSplitScreenPrimaryTask() {
- return getRootSplitScreenPrimaryTask() != null;
+ boolean isSplitScreenModeActivated() {
+ Task task = getRootSplitScreenPrimaryTask();
+ return task != null && task.hasChild();
}
ActivityStack getRootPinnedTask() {
@@ -2600,7 +2603,7 @@
}
amendWindowTapExcludeRegion(mTouchExcludeRegion);
// TODO(multi-display): Support docked stacks on secondary displays.
- if (mDisplayId == DEFAULT_DISPLAY && getRootSplitScreenPrimaryTask() != null) {
+ if (mDisplayId == DEFAULT_DISPLAY && isSplitScreenModeActivated()) {
mDividerControllerLocked.getTouchRegion(mTmpRect);
mTmpRegion.set(mTmpRect);
mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
@@ -2623,8 +2626,7 @@
// If the task is home stack and it is resizable and visible (top of its root task), we want
// to exclude the docked stack from touch so we need the entire screen area and not just a
// small portion which the home stack currently is resized to.
- if (task.isActivityTypeHome() && task.isVisible() && task.getStack().getTile() != null
- && task.isResizeable()) {
+ if (task.isActivityTypeHome() && task.isVisible() && task.isResizeable()) {
mDisplayContent.getBounds(mTmpRect);
} else {
task.getDimBounds(mTmpRect);
@@ -4328,15 +4330,8 @@
@VisibleForTesting
ActivityStack getTopStack() {
- // TODO(task-hierarchy): Just grab index -1 once tiles are in hierarchy.
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack child = mTaskContainers.getChildAt(i);
- if (child instanceof TaskTile) {
- continue;
- }
- return child;
- }
- return null;
+ final int count = mTaskContainers.getChildCount();
+ return count > 0 ? mTaskContainers.getChildAt(count - 1) : null;
}
int getIndexOf(ActivityStack stack) {
@@ -4375,10 +4370,6 @@
}
private void addStackReferenceIfNeeded(ActivityStack stack) {
- // TODO(task-hierarchy): Remove when tiles are in hierarchy.
- if (stack instanceof TaskTile) {
- return;
- }
if (stack.isActivityTypeHome()) {
if (mRootHomeTask != null) {
if (!stack.isDescendantOf(mRootHomeTask)) {
@@ -4390,27 +4381,26 @@
mRootHomeTask = stack;
}
}
+
+ if (!stack.isRootTask()) {
+ return;
+ }
final int windowingMode = stack.getWindowingMode();
if (windowingMode == WINDOWING_MODE_PINNED) {
if (mRootPinnedTask != null) {
- if (!stack.isDescendantOf(mRootPinnedTask)) {
- throw new IllegalArgumentException(
- "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
- + " already exist on display=" + this + " stack=" + stack);
- }
- } else {
- mRootPinnedTask = stack;
+ throw new IllegalArgumentException(
+ "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
+ + " already exist on display=" + this + " stack=" + stack);
}
+ mRootPinnedTask = stack;
} else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
if (mRootSplitScreenPrimaryTask != null) {
- if (!stack.isDescendantOf(mRootSplitScreenPrimaryTask)) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded:"
- + " split-screen-primary" + " stack=" + mRootSplitScreenPrimaryTask
- + " already exist on display=" + this + " stack=" + stack);
- }
- } else {
- mRootSplitScreenPrimaryTask = stack;
+ throw new IllegalArgumentException(
+ "addStackReferenceIfNeeded: split screen primary stack="
+ + mRootSplitScreenPrimaryTask
+ + " already exist on display=" + this + " stack=" + stack);
}
+ mRootSplitScreenPrimaryTask = stack;
}
}
@@ -4495,6 +4485,10 @@
*/
private int findPositionForStack(int requestedPosition, ActivityStack stack,
boolean adding) {
+ if (stack.isActivityTypeDream()) {
+ return POSITION_TOP;
+ }
+
if (stack.inPinnedWindowingMode()) {
return POSITION_TOP;
}
@@ -4645,10 +4639,9 @@
// Apps and their containers are not allowed to specify an orientation while using
// root tasks...except for the home stack if it is not resizable and currently
// visible (top of) its root task.
- if (mRootHomeTask != null && mRootHomeTask.isVisible()
- && mRootHomeTask.getTile() != null) {
+ if (mRootHomeTask != null && mRootHomeTask.isVisible()) {
final Task topMost = mRootHomeTask.getTopMostTask();
- final boolean resizable = topMost == null && topMost.isResizeable();
+ final boolean resizable = topMost != null && topMost.isResizeable();
if (!(resizable && mRootHomeTask.matchParentBounds())) {
final int orientation = mRootHomeTask.getOrientation();
if (orientation != SCREEN_ORIENTATION_UNSET) {
@@ -4792,17 +4785,6 @@
mSplitScreenDividerAnchor = null;
}
}
-
- @Override
- void onChildPositionChanged(WindowContainer child) {
- // TODO(task-hierarchy): Move functionality to TaskTile when it's a proper parent.
- TaskTile tile = ((ActivityStack) child).getTile();
- if (tile == null) {
- return;
- }
- mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
- tile, false /* force */);
- }
}
private class WindowContainers extends DisplayChildWindowContainer<WindowContainer> {
@@ -4983,7 +4965,7 @@
private boolean skipImeWindowsDuringTraversal(DisplayContent dc) {
// We skip IME windows so they're processed just above their target, except
// in split-screen mode where we process the IME containers above the docked divider.
- return dc.mInputMethodTarget != null && !dc.hasSplitScreenPrimaryTask();
+ return dc.mInputMethodTarget != null && !dc.isSplitScreenModeActivated();
}
/** Like {@link #forAllWindows}, but ignores {@link #skipImeWindowsDuringTraversal} */
@@ -5657,6 +5639,14 @@
mAtmService.updateSleepIfNeededLocked();
}
+ void addStackReferenceIfNeeded(ActivityStack stack) {
+ mTaskContainers.addStackReferenceIfNeeded(stack);
+ }
+
+ void removeStackReferenceIfNeeded(ActivityStack stack) {
+ mTaskContainers.removeStackReferenceIfNeeded(stack);
+ }
+
void onStackRemoved(ActivityStack stack) {
if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
Slog.v(TAG_STACK, "removeStack: detaching " + stack + " from displayId=" + mDisplayId);
@@ -5703,6 +5693,14 @@
"positionStackAt: Can only have one task on display=" + this);
}
+ final boolean movingToTop = wasContained && position >= getStackCount() - 1;
+ // Reset mPreferredTopFocusableStack before positioning to top or {@link
+ // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top
+ // resumed activity.
+ if (movingToTop && stack.isFocusable()) {
+ mPreferredTopFocusableStack = null;
+ }
+
// Since positionChildAt() is called during the creation process of pinned stacks,
// ActivityStack#getStack() can be null.
positionStackAt(position, stack, includingParents);
@@ -5712,7 +5710,7 @@
// we are looking for top focusable stack. The condition {@code wasContained} restricts the
// preferred stack is set only when moving an existing stack to top instead of adding a new
// stack that may be too early (e.g. in the middle of launching or reparenting).
- if (wasContained && position >= getStackCount() - 1 && stack.isFocusableAndVisible()) {
+ if (movingToTop && stack.isFocusableAndVisible()) {
mPreferredTopFocusableStack = stack;
} else if (mPreferredTopFocusableStack == stack) {
mPreferredTopFocusableStack = null;
@@ -5755,18 +5753,54 @@
/**
* Returns an existing stack compatible with the windowing mode and activity type or creates one
* if a compatible stack doesn't exist.
+ * @see #getOrCreateStack(int, int, boolean, Intent, Task, boolean)
+ */
+ ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop) {
+ return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
+ null /* candidateTask */, false /* createdByOrganizer */);
+ }
+
+ /**
+ * When two level tasks are required for given windowing mode and activity type, returns an
+ * existing compatible root task or creates a new one.
+ * For one level task, the candidate task would be reused to also be the root task or create
+ * a new root task if no candidate task.
* @see #getStack(int, int)
* @see #createStack(int, int, boolean)
*/
- ActivityStack getOrCreateStack(int windowingMode, int activityType,
- boolean onTop) {
+ ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop,
+ Intent intent, Task candidateTask, boolean createdByOrganizer) {
if (!alwaysCreateStack(windowingMode, activityType)) {
ActivityStack stack = getStack(windowingMode, activityType);
if (stack != null) {
return stack;
}
+ } else if (candidateTask != null) {
+ final ActivityStack stack = (ActivityStack) candidateTask;
+ final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
+ if (isSplitScreenModeActivated()) {
+ final Task splitRootSecondary = getTask(t -> t.mCreatedByOrganizer && t.isRootTask()
+ && t.inSplitScreenSecondaryWindowingMode());
+ if (stack.getParent() == null) {
+ splitRootSecondary.addChild(stack, position);
+ } else if (stack.getParent() != splitRootSecondary) {
+ stack.reparent(splitRootSecondary, position);
+ }
+ } else if (stack.getDisplay() != this || !stack.isRootTask()) {
+ if (stack.getParent() == null) {
+ addStack(stack, position);
+ } else {
+ stack.reparent(this, onTop);
+ }
+ }
+ // Update windowing mode if necessary, e.g. moving a pinned task to fullscreen.
+ if (candidateTask.getWindowingMode() != windowingMode) {
+ candidateTask.setWindowingMode(windowingMode);
+ }
+ return stack;
}
- return createStack(windowingMode, activityType, onTop);
+ return createStack(windowingMode, activityType, onTop, null /*info*/, intent,
+ createdByOrganizer);
}
/**
@@ -5784,7 +5818,8 @@
// UNDEFINED windowing mode is a valid result and means that the new stack will inherit
// it's display's windowing mode.
windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
- return getOrCreateStack(windowingMode, activityType, onTop);
+ return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
+ candidateTask, false /* createdByOrganizer */);
}
@VisibleForTesting
@@ -5793,7 +5828,8 @@
}
ActivityStack createStack(int windowingMode, int activityType, boolean onTop) {
- return createStack(windowingMode, activityType, onTop, null /*info*/, null /*intent*/);
+ return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */,
+ false /* createdByOrganizer */);
}
/**
@@ -5805,25 +5841,29 @@
* {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will
* be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
* @param onTop If true the stack will be created at the top of the display, else at the bottom.
+ * @param info The started activity info.
+ * @param intent The intent that started this task.
+ * @param createdByOrganizer @{code true} if this is created by task organizer, @{code false}
+ * otherwise.
* @return The newly created stack.
*/
ActivityStack createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
- Intent intent) {
+ Intent intent, boolean createdByOrganizer) {
if (mSingleTaskInstance && getStackCount() > 0) {
// Create stack on default display instead since this display can only contain 1 stack.
// TODO: Kinda a hack, but better that having the decision at each call point. Hoping
// this goes away once ActivityView is no longer using virtual displays.
return mRootWindowContainer.getDefaultDisplay().createStack(
- windowingMode, activityType, onTop, info, intent);
+ windowingMode, activityType, onTop, info, intent, createdByOrganizer);
}
- if (activityType == ACTIVITY_TYPE_UNDEFINED) {
+ if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) {
// Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
- // anything else should be passing it in anyways...
+ // anything else should be passing it in anyways...except for the task organizer.
activityType = ACTIVITY_TYPE_STANDARD;
}
- if (activityType != ACTIVITY_TYPE_STANDARD) {
+ if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) {
// For now there can be only one stack of a particular non-standard activity type on a
// display. So, get that ignoring whatever windowing mode it is currently in.
ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
@@ -5842,39 +5882,39 @@
}
final int stackId = getNextStackId();
- return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent);
+ return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent,
+ createdByOrganizer);
}
- /** @return the tile to create the next stack in. */
- private TaskTile updateLaunchTile(int windowingMode) {
+ /** @return the root task to create the next task in. */
+ private Task updateLaunchRootTask(int windowingMode) {
if (!isSplitScreenWindowingMode(windowingMode)) {
- // Only split-screen windowing modes interact with tiles.
+ // Only split-screen windowing modes can do this currently...
return null;
}
for (int i = getStackCount() - 1; i >= 0; --i) {
- final TaskTile t = getStackAt(i).asTile();
- if (t == null || t.getRequestedOverrideWindowingMode() != windowingMode) {
+ final Task t = getStackAt(i);
+ if (!t.mCreatedByOrganizer || t.getRequestedOverrideWindowingMode() != windowingMode) {
continue;
}
- // If not already set, pick a launch tile which is not the one we are launching
- // into.
- if (mLaunchTile == null) {
+ // If not already set, pick a launch root which is not the one we are launching into.
+ if (mLaunchRootTask == null) {
for (int j = 0, n = getStackCount(); j < n; ++j) {
- TaskTile tt = getStackAt(j).asTile();
- if (tt != t) {
- mLaunchTile = tt;
+ final Task tt = getStackAt(j);
+ if (tt.mCreatedByOrganizer && tt != t) {
+ mLaunchRootTask = tt;
break;
}
}
}
return t;
}
- return mLaunchTile;
+ return mLaunchRootTask;
}
@VisibleForTesting
- ActivityStack createStackUnchecked(int windowingMode, int activityType,
- int stackId, boolean onTop, ActivityInfo info, Intent intent) {
+ ActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId,
+ boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) {
if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
+ "activity type.");
@@ -5884,19 +5924,25 @@
info.applicationInfo = new ApplicationInfo();
}
- TaskTile tile = updateLaunchTile(windowingMode);
- if (tile != null) {
- // Since this stack will be put into a tile, its windowingMode will be inherited.
+ // Task created by organizer are added as root.
+ Task launchRootTask = createdByOrganizer ? null : updateLaunchRootTask(windowingMode);
+ if (launchRootTask != null) {
+ // Since this stack will be put into a root task, its windowingMode will be inherited.
windowingMode = WINDOWING_MODE_UNDEFINED;
}
+
final ActivityStack stack = (ActivityStack) Task.create(mAtmService, stackId, activityType,
- info, intent);
- addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
- stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
- false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
- true /* creating */);
- if (tile != null) {
- tile.addChild(stack, 0 /* index */);
+ info, intent, createdByOrganizer);
+ if (launchRootTask != null) {
+ launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
+ if (onTop) {
+ positionStackAtTop((ActivityStack) launchRootTask, false /* includingParents */);
+ }
+ } else {
+ addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
+ stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
+ false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
+ true /* creating */);
}
return stack;
}
@@ -6031,7 +6077,7 @@
mTmpFindTaskResult.clear();
for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = getStackAt(stackNdx);
- if (!r.hasCompatibleActivityType(stack)) {
+ if (!r.hasCompatibleActivityType(stack) && stack.isLeafTask()) {
if (DEBUG_TASKS) {
Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
}
@@ -6103,7 +6149,15 @@
final int activityType = activityTypes[j];
for (int i = getStackCount() - 1; i >= 0; --i) {
final ActivityStack stack = getStackAt(i);
- if (stack.getActivityType() == activityType) {
+ // Collect the root tasks that are currently being organized.
+ if (stack.isOrganized()) {
+ for (int k = stack.getChildCount() - 1; k >= 0; --k) {
+ final ActivityStack childStack = (ActivityStack) stack.getChildAt(k);
+ if (childStack.getActivityType() == activityType) {
+ stacks.add(childStack);
+ }
+ }
+ } else if (stack.getActivityType() == activityType) {
stacks.add(stack);
}
}
@@ -6117,13 +6171,8 @@
void onSplitScreenModeDismissed() {
mAtmService.deferWindowLayout();
try {
- mLaunchTile = null;
- for (int i = getStackCount() - 1; i >= 0; --i) {
- final TaskTile t = getStackAt(i).asTile();
- if (t != null) {
- t.removeAllChildren();
- }
- }
+ mLaunchRootTask = null;
+ moveSplitScreenTasksToFullScreen();
} finally {
final ActivityStack topFullscreenStack =
getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -6141,6 +6190,24 @@
}
}
+ private void moveSplitScreenTasksToFullScreen() {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTmpTasks.clear();
+ forAllTasks(task -> {
+ if (task.mCreatedByOrganizer && task.inSplitScreenWindowingMode() && task.hasChild()) {
+ mTmpTasks.add(task);
+ }
+ });
+
+ for (int i = mTmpTasks.size() - 1; i >= 0; i--) {
+ final Task root = mTmpTasks.get(i);
+ for (int j = 0; j < root.getChildCount(); j++) {
+ wct.reparent(root.getChildAt(j).mRemoteToken, null, true /* toTop */);
+ }
+ }
+ mAtmService.mWindowOrganizerController.applyTransaction(wct);
+ }
+
/**
* Returns true if the {@param windowingMode} is supported based on other parameters passed in.
* @param windowingMode The windowing mode we are checking support for.
@@ -6253,7 +6320,7 @@
}
}
- final boolean inSplitScreenMode = hasSplitScreenPrimaryTask();
+ final boolean inSplitScreenMode = isSplitScreenModeActivated();
if (!inSplitScreenMode
&& windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
// Switch to the display's windowing mode if we are not in split-screen mode and we are
@@ -6278,10 +6345,6 @@
}
boolean isTopNotPinnedStack(ActivityStack stack) {
- // TODO(task-hierarchy): Remove when tiles are in hierarchy.
- if (stack instanceof TaskTile) {
- return false;
- }
for (int i = getStackCount() - 1; i >= 0; --i) {
final ActivityStack current = getStackAt(i);
if (!current.inPinnedWindowingMode()) {
@@ -6503,7 +6566,7 @@
// If default display is in split-window mode, set windowing mode of the stack
// to split-screen secondary. Otherwise, set the windowing mode to undefined by
// default to let stack inherited the windowing mode from the new display.
- final int windowingMode = toDisplay.hasSplitScreenPrimaryTask()
+ final int windowingMode = toDisplay.isSplitScreenModeActivated()
? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_UNDEFINED;
stack.reparent(toDisplay, true /* onTop */);
@@ -6607,27 +6670,35 @@
* already top-most.
*/
ActivityStack getStackAbove(ActivityStack stack) {
- final int stackIndex = getIndexOf(stack) + 1;
- return (stackIndex < getStackCount()) ? getStackAt(stackIndex) : null;
+ final WindowContainer wc = stack.getParent();
+ final int index = wc.mChildren.indexOf(stack) + 1;
+ return (index < wc.mChildren.size()) ? (ActivityStack) wc.mChildren.get(index) : null;
}
/**
* Adjusts the {@param stack} behind the last visible stack in the display if necessary.
* Generally used in conjunction with {@link #moveStackBehindStack}.
*/
+ // TODO(b/151575894): Remove special stack movement methods.
void moveStackBehindBottomMostVisibleStack(ActivityStack stack) {
if (stack.shouldBeVisible(null)) {
// Skip if the stack is already visible
return;
}
- // Move the stack to the bottom to not affect the following visibility checks
- positionStackAtBottom(stack);
+ final boolean isRootTask = stack.isRootTask();
+ if (isRootTask) {
+ // Move the stack to the bottom to not affect the following visibility checks
+ positionStackAtBottom(stack);
+ } else {
+ stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
+ }
// Find the next position where the stack should be placed
- final int numStacks = getStackCount();
+ final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount();
for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
- final ActivityStack s = getStackAt(stackNdx);
+ final ActivityStack s = isRootTask ? getStackAt(stackNdx)
+ : (ActivityStack) stack.getParent().getChildAt(stackNdx);
if (s == stack) {
continue;
}
@@ -6636,7 +6707,12 @@
|| winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
if (s.shouldBeVisible(null) && isValidWindowingMode) {
// Move the provided stack to behind this stack
- positionStackAt(stack, Math.max(0, stackNdx - 1));
+ final int position = Math.max(0, stackNdx - 1);
+ if (isRootTask) {
+ positionStackAt(stack, position);
+ } else {
+ stack.getParent().positionChildAt(position, stack, false /*includingParents */);
+ }
break;
}
}
@@ -6652,15 +6728,25 @@
return;
}
+ final WindowContainer parent = stack.getParent();
+ if (parent == null || parent != behindStack.getParent()) {
+ return;
+ }
+
// Note that positionChildAt will first remove the given stack before inserting into the
// list, so we need to adjust the insertion index to account for the removed index
// TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
// position internally
- final int stackIndex = getIndexOf(stack);
- final int behindStackIndex = getIndexOf(behindStack);
+ final int stackIndex = parent.mChildren.indexOf(stack);
+ final int behindStackIndex = parent.mChildren.indexOf(behindStack);
final int insertIndex = stackIndex <= behindStackIndex
? behindStackIndex - 1 : behindStackIndex;
- positionStackAt(stack, Math.max(0, insertIndex));
+ final int position = Math.max(0, insertIndex);
+ if (stack.isRootTask()) {
+ positionStackAt(stack, position);
+ } else {
+ parent.positionChildAt(position, stack, false /* includingParents */);
+ }
}
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
@@ -6697,19 +6783,6 @@
return getHomeActivityForUser(mRootWindowContainer.mCurrentUser);
}
- // TODO(task-hierarchy): Remove when tiles are in hierarchy.
- void addTile(TaskTile tile) {
- mTaskContainers.addChild(tile, POSITION_BOTTOM);
- ITaskOrganizer organizer = mAtmService.mTaskOrganizerController.getTaskOrganizer(
- tile.getWindowingMode());
- tile.setTaskOrganizer(organizer);
- }
-
- // TODO(task-hierarchy): Remove when tiles are in hierarchy.
- void removeTile(TaskTile tile) {
- mTaskContainers.removeChild(tile);
- }
-
@Nullable
ActivityRecord getHomeActivityForUser(int userId) {
final ActivityStack homeStack = getRootHomeTask();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 5e88fb0..4da4a79 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1527,25 +1527,7 @@
&& (mNotificationShade.getAttrs().privateFlags
& PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
- // When the navigation bar isn't visible, we put up a fake input window to catch all
- // touch events. This way we can detect when the user presses anywhere to bring back the
- // nav bar and ensure the application doesn't see the event.
- if (navVisible || navAllowedHidden) {
- if (mInputConsumer != null) {
- mInputConsumer.dismiss();
- mHandler.sendMessage(
- mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
- mInputConsumer = null;
- }
- } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
- mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
- mHandler.getLooper(),
- INPUT_CONSUMER_NAVIGATION,
- HideNavInputEventReceiver::new);
- // As long as mInputConsumer is active, hover events are not dispatched to the app
- // and the pointer icon is likely to become stale. Hide it to avoid confusion.
- InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
- }
+ updateHideNavInputEventReceiver(navVisible, navAllowedHidden);
// For purposes of positioning and showing the nav bar, if we have decided that it can't
// be hidden (because of the screen aspect ratio), then take that into account.
@@ -1567,6 +1549,28 @@
mLastNotificationShadeForcesShowingNavigation = notificationShadeForcesShowingNavigation;
}
+ void updateHideNavInputEventReceiver(boolean navVisible, boolean navAllowedHidden) {
+ // When the navigation bar isn't visible, we put up a fake input window to catch all
+ // touch events. This way we can detect when the user presses anywhere to bring back the
+ // nav bar and ensure the application doesn't see the event.
+ if (navVisible || navAllowedHidden) {
+ if (mInputConsumer != null) {
+ mInputConsumer.dismiss();
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
+ mInputConsumer = null;
+ }
+ } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
+ mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
+ mHandler.getLooper(),
+ INPUT_CONSUMER_NAVIGATION,
+ HideNavInputEventReceiver::new);
+ // As long as mInputConsumer is active, hover events are not dispatched to the app
+ // and the pointer icon is likely to become stale. Hide it to avoid confusion.
+ InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
+ }
+ }
+
private static void updateInsetsStateForDisplayCutout(DisplayFrames displayFrames,
InsetsState state) {
if (displayFrames.mDisplayCutout.getDisplayCutout().isEmpty()) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index bb02789..ac6e75c 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -25,6 +25,7 @@
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
@@ -91,6 +92,12 @@
|| focusedWin != getNavControlTarget(focusedWin)
|| focusedWin.getRequestedInsetsState().getSource(ITYPE_NAVIGATION_BAR)
.isVisible());
+ updateHideNavInputEventReceiver();
+ }
+
+ private void updateHideNavInputEventReceiver() {
+ mPolicy.updateHideNavInputEventReceiver(!isHidden(ITYPE_NAVIGATION_BAR),
+ mFocusedWin.mAttrs.insetsFlags.behavior != BEHAVIOR_SHOW_BARS_BY_TOUCH);
}
boolean isHidden(@InternalInsetsType int type) {
@@ -169,6 +176,7 @@
if (windowState == getNavControlTarget(mFocusedWin)) {
mNavBar.setVisible(state.getSource(ITYPE_NAVIGATION_BAR).isVisible());
}
+ updateHideNavInputEventReceiver();
}
/**
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 44034ed..6b39fd2 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -406,12 +406,11 @@
// show on top of the lock screen. In this can we want to dismiss the docked
// stack since it will be complicated/risky to try to put the activity on top
// of the lock screen in the right fullscreen configuration.
- final ActivityStack stack =
- mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
- if (stack == null) {
+ final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+ if (!display.isSplitScreenModeActivated()) {
return;
}
- mRootWindowContainer.getDefaultDisplay().onSplitScreenModeDismissed();
+ display.onSplitScreenModeDismissed();
}
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index bd5666d..244ba82 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -21,6 +21,7 @@
import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -1298,6 +1299,7 @@
switch (task.getActivityType()) {
case ACTIVITY_TYPE_HOME:
case ACTIVITY_TYPE_RECENTS:
+ case ACTIVITY_TYPE_DREAM:
// Ignore certain activity types completely
return false;
case ACTIVITY_TYPE_ASSISTANT:
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index adafdec..9089240 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -388,11 +388,12 @@
// surfaces needs to be done immediately.
mWindowManager.executeAppTransition();
- if (targetStack.getTile() != null) {
+ final Task rootTask = targetStack.getRootTask();
+ if (rootTask.isOrganized()) {
// Client state may have changed during the recents animation, so force
// send task info so the client can synchronize its state.
mService.mTaskOrganizerController.dispatchTaskInfoChanged(
- targetStack.mTile, true /* force */);
+ rootTask, true /* force */);
}
} catch (Exception e) {
Slog.e(TAG, "Failed to clean up recents activity", e);
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 45f8a15..420997a 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -20,7 +20,6 @@
import static com.android.server.wm.ActivityStack.TAG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
import android.app.ActivityOptions;
import android.content.Intent;
@@ -233,29 +232,6 @@
}
final ActivityTaskManagerService atmService = mTargetStack.mAtmService;
- final ArrayList<Task> createdTasks = new ArrayList<>();
- while (!mPendingReparentActivities.isEmpty()) {
- final ActivityRecord r = mPendingReparentActivities.remove(0);
- final ActivityRecord bottom = mTargetStack.getBottomMostActivity();
- final Task targetTask;
- if (bottom != null && r.taskAffinity.equals(bottom.getTask().affinity)) {
- // If the activity currently at the bottom has the same task affinity as
- // the one we are moving, then merge it into the same task.
- targetTask = bottom.getTask();
- if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
- + r + " out to bottom task " + targetTask);
- } else {
- targetTask = mTargetStack.reuseOrCreateTask(
- r.info, null /*intent*/, false /*toTop*/);
- targetTask.affinityIntent = r.intent;
- createdTasks.add(targetTask);
- if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
- + r + " out to new task " + targetTask);
- }
- r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded");
- atmService.mStackSupervisor.mRecentTasks.add(targetTask);
- }
-
DisplayContent display = mTargetStack.getDisplay();
final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance();
if (singleTaskInstanceDisplay) {
@@ -264,16 +240,33 @@
final int windowingMode = mTargetStack.getWindowingMode();
final int activityType = mTargetStack.getActivityType();
- if (!singleTaskInstanceDisplay && !display.alwaysCreateStack(windowingMode, activityType)) {
- return;
- }
- while (!createdTasks.isEmpty()) {
- final Task targetTask = createdTasks.remove(createdTasks.size() - 1);
- final ActivityStack targetStack = display.getOrCreateStack(
- windowingMode, activityType, false /* onTop */);
- targetTask.reparent(targetStack, false /* toTop */, REPARENT_LEAVE_STACK_IN_PLACE,
- false /* animate */, true /* deferResume */, "resetTargetTask");
+ while (!mPendingReparentActivities.isEmpty()) {
+ final ActivityRecord r = mPendingReparentActivities.remove(0);
+ final boolean alwaysCreateTask = DisplayContent.alwaysCreateStack(windowingMode,
+ activityType);
+ final Task task = alwaysCreateTask
+ ? display.getBottomMostTask() : mTargetStack.getBottomMostTask();
+ Task targetTask = null;
+ if (task != null && r.taskAffinity.equals(task.affinity)) {
+ // If the activity currently at the bottom has the same task affinity as
+ // the one we are moving, then merge it into the same task.
+ targetTask = task;
+ if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
+ + r + " out to bottom task " + targetTask);
+ }
+ if (targetTask == null) {
+ if (alwaysCreateTask) {
+ targetTask = display.getOrCreateStack(windowingMode, activityType,
+ false /* onTop */);
+ } else {
+ targetTask = mTargetStack.reuseOrCreateTask(r.info, null /*intent*/,
+ false /*toTop*/);
+ }
+ targetTask.affinityIntent = r.intent;
+ }
+ r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded");
+ atmService.mStackSupervisor.mRecentTasks.add(targetTask);
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ebf1bc9..b2920ee 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -18,6 +18,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -1970,8 +1971,7 @@
final int focusStackId = topFocusedStack != null
? topFocusedStack.getRootTaskId() : INVALID_TASK_ID;
// We dismiss the docked stack whenever we switch users.
- final ActivityStack dockedStack = getDefaultDisplay().getRootSplitScreenPrimaryTask();
- if (dockedStack != null) {
+ if (getDefaultDisplay().isSplitScreenModeActivated()) {
getDefaultDisplay().onSplitScreenModeDismissed();
}
// Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
@@ -2110,20 +2110,18 @@
final ActivityStack stack;
if (singleActivity) {
stack = r.getRootTask();
+ stack.setWindowingMode(WINDOWING_MODE_PINNED);
} else {
- // In the case of multiple activities, we will create a new stack for it and then
- // move the PIP activity into the stack.
- // We will then perform a windowing mode change for both scenarios.
- stack = display.createStack(
- r.getRootTask().getRequestedOverrideWindowingMode(),
- r.getActivityType(), ON_TOP, r.info, r.intent);
+ // In the case of multiple activities, we will create a new task for it and then
+ // move the PIP activity into the task.
+ stack = display.createStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP,
+ r.info, r.intent, false /* createdByOrganizer */);
+
// There are multiple activities in the task and moving the top activity should
// reveal/leave the other activities in their original task.
r.reparent(stack, MAX_VALUE, "moveActivityToStack");
}
- stack.setWindowingMode(WINDOWING_MODE_PINNED);
-
// Reset the state that indicates it can enter PiP while pausing after we've moved it
// to the pinned stack
r.supportsEnterPipOnTaskSwitch = false;
@@ -2799,16 +2797,19 @@
if (stack == null && r != null) {
stack = r.getRootTask();
}
+ int windowingMode = launchParams != null ? launchParams.mWindowingMode
+ : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
if (stack != null) {
display = stack.getDisplay();
if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
- int windowingMode = launchParams != null ? launchParams.mWindowingMode
- : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
windowingMode = display.resolveWindowingMode(r, options, candidateTask,
activityType);
}
- if (stack.isCompatible(windowingMode, activityType)) {
+ // Always allow organized tasks that created by organizer since the activity type
+ // of an organized task is decided by the activity type of its top child, which
+ // could be incompatible with the given windowing mode and activity type.
+ if (stack.isCompatible(windowingMode, activityType) || stack.mCreatedByOrganizer) {
return stack;
}
if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
@@ -2826,6 +2827,10 @@
if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
display = getDefaultDisplay();
+ if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+ windowingMode = display.resolveWindowingMode(r, options, candidateTask,
+ activityType);
+ }
}
return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
@@ -2915,11 +2920,10 @@
case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
+ case ACTIVITY_TYPE_DREAM: return r.isActivityTypeDream();
}
- // TODO(task-hierarchy): Find another way to differentiate tile from normal stack once it is
- // part of the hierarchy
- if (stack instanceof TaskTile) {
- // Don't launch directly into tiles.
+ if (stack.mCreatedByOrganizer) {
+ // Don't launch directly into task created by organizer...but why can't we?
return false;
}
// There is a 1-to-1 relationship between stack and task when not in
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index bfe3147..f19c106 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -117,7 +117,6 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
@@ -492,6 +491,17 @@
PictureInPictureParams mPictureInPictureParams = new PictureInPictureParams.Builder().build();
/**
+ * This task was created by the task organizer which has the following implementations.
+ * <ul>
+ * <lis>The task won't be removed when it is empty. Removal has to be an explicit request
+ * from the task organizer.</li>
+ * <li>Unlike other non-root tasks, it's direct children are visible to the task
+ * organizer for ordering purposes.</li>
+ * </ul>
+ */
+ boolean mCreatedByOrganizer;
+
+ /**
* Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
* ActivityInfo, Intent, TaskDescription)} instead.
*/
@@ -1356,7 +1366,7 @@
if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
return applicationType;
}
- return getChildAt(0).getActivityType();
+ return getTopChild().getActivityType();
}
@Override
@@ -1369,6 +1379,12 @@
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addChild: %s at top.", this);
+ // A rootable task that is now being added to be the child of an organized task. Making
+ // sure the stack references is keep updated.
+ if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
+ mDisplayContent.addStackReferenceIfNeeded((ActivityStack) child);
+ }
+
// Make sure the list of display UID whitelists is updated
// now that this record is in a new task.
mRootWindowContainer.updateUIDsPresentOnDisplay();
@@ -1409,6 +1425,11 @@
@Override
void removeChild(WindowContainer child) {
+ // A rootable child task that is now being removed from an organized task. Making sure
+ // the stack references is keep updated.
+ if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
+ mDisplayContent.removeStackReferenceIfNeeded((ActivityStack) child);
+ }
removeChild(child, "removeChild");
}
@@ -1456,8 +1477,9 @@
mStackSupervisor.removeTask(this, false /* killProcess */,
!REMOVE_FROM_RECENTS, reason);
}
- } else if (!mReuseTask) {
+ } else if (!mReuseTask && !mCreatedByOrganizer) {
// Remove entire task if it doesn't have any activity left and it isn't marked for reuse
+ // or created by task organizer.
if (!isRootTask) {
getStack().removeChild(this, reason);
}
@@ -1869,7 +1891,12 @@
final Task parentTask = getParent().asTask();
if (parentTask != null) {
parentTask.onActivityStateChanged(record, state, reason);
- return;
+ // We still want to update the resumed activity if the parent task is created by
+ // organizer in order to keep the information synced once got reparented out from the
+ // organized task.
+ if (!parentTask.mCreatedByOrganizer) {
+ return;
+ }
}
if (record == mResumedActivity && state != RESUMED) {
@@ -2303,18 +2330,30 @@
return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
}
- void resolveTileOverrideConfiguration(Configuration newParentConfig) {
+ private void resolveOrganizedOverrideConfiguration(Configuration newParentConfig) {
super.resolveOverrideConfiguration(newParentConfig);
+ if (!isOrganized()) {
+ return;
+ }
+
+ final Task root = getRootTask();
+ if (root == this) {
+ return;
+ }
+
+ // Ensure to have the same windowing mode for the child tasks that controlled by task org.
+ getResolvedOverrideConfiguration().windowConfiguration
+ .setWindowingMode(root.getWindowingMode());
}
@Override
void resolveOverrideConfiguration(Configuration newParentConfig) {
- if (!isLeafTask()) {
- resolveTileOverrideConfiguration(newParentConfig);
+ if (!isLeafTask() || mCreatedByOrganizer) {
+ resolveOrganizedOverrideConfiguration(newParentConfig);
return;
}
mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
- resolveTileOverrideConfiguration(newParentConfig);
+ resolveOrganizedOverrideConfiguration(newParentConfig);
int windowingMode =
getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
@@ -2403,7 +2442,9 @@
}
Rect updateOverrideConfigurationFromLaunchBounds() {
- final Rect bounds = getLaunchBounds();
+ // If the task is controlled by another organized task, do not set override
+ // configurations and let its parent (organized task) to control it;
+ final Rect bounds = isOrganized() && !isRootTask() ? null : getLaunchBounds();
setBounds(bounds);
if (bounds != null && !bounds.isEmpty()) {
// TODO: Review if we actually want to do this - we are setting the launch bounds
@@ -2587,7 +2628,7 @@
// preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid.
if (suggestedPosition == POSITION_BOTTOM && minPosition == 0) {
return POSITION_BOTTOM;
- } else if (suggestedPosition == POSITION_TOP && maxPosition == (size - 1)) {
+ } else if (suggestedPosition == POSITION_TOP && maxPosition >= (size - 1)) {
return POSITION_TOP;
}
// Reset position based on minimum/maximum possible positions.
@@ -3385,14 +3426,12 @@
info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
info.configuration.setTo(getConfiguration());
info.token = mRemoteToken;
- // Get's the first non-undefined activity type among this and children. Can't use
- // configuration.windowConfiguration because that would only be this level.
- info.topActivityType = getActivityType();
//TODO (AM refactor): Just use local once updateEffectiveIntent is run during all child
// order changes.
final Task top = getTopMostTask();
info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
+ info.topActivityType = top.getActivityType();
if (mPictureInPictureParams.empty()) {
info.pictureInPictureParams = null;
@@ -3423,10 +3462,6 @@
return this;
}
- TaskTile asTile() {
- return null;
- }
-
// TODO(task-merge): Figure-out how this should work with hierarchy tasks.
boolean shouldBeVisible(ActivityRecord starting) {
return true;
@@ -3722,8 +3757,9 @@
}
static Task create(ActivityTaskManagerService service, int taskId, int activityType,
- ActivityInfo info, Intent intent) {
- return getTaskFactory().create(service, taskId, activityType, info, intent);
+ ActivityInfo info, Intent intent, boolean createdByOrganizer) {
+ return getTaskFactory().create(service, taskId, activityType, info, intent,
+ createdByOrganizer);
}
static Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
@@ -3745,8 +3781,9 @@
*/
static class TaskFactory {
Task create(ActivityTaskManagerService service, int taskId, int activityType,
- ActivityInfo info, Intent intent) {
- return new ActivityStack(service, taskId, activityType, info, intent);
+ ActivityInfo info, Intent intent, boolean createdByOrganizer) {
+ return new ActivityStack(service, taskId, activityType, info, intent,
+ createdByOrganizer);
}
Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
@@ -4022,21 +4059,29 @@
@Override
boolean isOrganized() {
final Task rootTask = getRootTask();
- // if the rootTask is a "child" of a tile, then don't consider it a root task.
- // TODO: remove this along with removing tile.
- if (((ActivityStack) rootTask).getTile() != null) {
+ if (rootTask.mTaskOrganizer == null) {
+ // You are obviously not organized...
return false;
}
- return rootTask == this && rootTask.mTaskOrganizer != null;
+ if (rootTask == this) {
+ // Root tasks can be organized.
+ return true;
+ }
+ if (rootTask.mCreatedByOrganizer && getParent() == rootTask) {
+ // Direct children of tasks added by the organizer can the organized.
+ return true;
+ }
+
+ return false;
}
@Override
protected void reparentSurfaceControl(SurfaceControl.Transaction t, SurfaceControl newParent) {
/**
- * Avoid yanking back control from the TaskOrganizer, which has presumably reparented the
- * Surface in to its own hierarchy.
+ * Avoid reparenting SurfaceControl of the organized tasks that are always on top, since
+ * the surfaces should be controlled by the organizer itself, like bubbles.
*/
- if (isOrganized()) {
+ if (isOrganized() && isAlwaysOnTop()) {
return;
}
super.reparentSurfaceControl(t, newParent);
@@ -4071,6 +4116,9 @@
mTaskOrganizer = null;
mLastTaskOrganizerWindowingMode = -1;
onTaskOrganizerChanged();
+ if (mCreatedByOrganizer) {
+ removeImmediately();
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 5523678..05b721b 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -27,13 +28,14 @@
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import android.window.ITaskOrganizerController;
import android.window.ITaskOrganizer;
+import android.window.ITaskOrganizerController;
import android.window.IWindowContainer;
import com.android.internal.util.ArrayUtils;
@@ -255,11 +257,12 @@
if (display == null) {
return null;
}
- final int nextId = display.getNextStackId();
- TaskTile tile = new TaskTile(mService, nextId, windowingMode);
- display.addTile(tile);
- RunningTaskInfo out = tile.getTaskInfo();
- mLastSentTaskInfos.put(tile, out);
+
+ final Task task = display.getOrCreateStack(windowingMode, ACTIVITY_TYPE_UNDEFINED,
+ false /* onTop */, new Intent(), null /* candidateTask */,
+ true /* createdByOrganizer */);
+ RunningTaskInfo out = task.getTaskInfo();
+ mLastSentTaskInfos.put(task, out);
return out;
}
} finally {
@@ -273,11 +276,13 @@
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- TaskTile tile = TaskTile.forToken(token.asBinder());
- if (tile == null) {
- return false;
+ final Task task = WindowContainer.fromBinder(token.asBinder()).asTask();
+ if (task == null) return false;
+ if (!task.mCreatedByOrganizer) {
+ throw new IllegalArgumentException(
+ "Attempt to delete task not created by organizer task=" + task);
}
- tile.removeImmediately();
+ task.removeImmediately();
return true;
}
} finally {
@@ -358,12 +363,7 @@
if (task == null) {
return null;
}
- ActivityStack rootTask = (ActivityStack) task.getRootTask();
- final TaskTile tile = rootTask.getTile();
- if (tile != null) {
- rootTask = tile;
- }
- return rootTask.mRemoteToken;
+ return task.getRootTask().mRemoteToken;
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -371,7 +371,7 @@
}
@Override
- public void setLaunchRoot(int displayId, @Nullable IWindowContainer tile) {
+ public void setLaunchRoot(int displayId, @Nullable IWindowContainer token) {
enforceStackPermission("setLaunchRoot()");
final long origId = Binder.clearCallingIdentity();
try {
@@ -380,16 +380,21 @@
if (display == null) {
return;
}
- TaskTile taskTile = tile == null ? null : TaskTile.forToken(tile.asBinder());
- if (taskTile == null) {
- display.mLaunchTile = null;
+ Task task = token == null
+ ? null : WindowContainer.fromBinder(token.asBinder()).asTask();
+ if (task == null) {
+ display.mLaunchRootTask = null;
return;
}
- if (taskTile.getDisplay() != display) {
- throw new RuntimeException("Can't set launch root for display " + displayId
- + " to task on display " + taskTile.getDisplay().getDisplayId());
+ if (!task.mCreatedByOrganizer) {
+ throw new IllegalArgumentException("Attempt to set task not created by "
+ + "organizer as launch root task=" + task);
}
- display.mLaunchTile = taskTile;
+ if (task.getDisplayContent() != display) {
+ throw new RuntimeException("Can't set launch root for display " + displayId
+ + " to task on display " + task.getDisplayContent().getDisplayId());
+ }
+ display.mLaunchRootTask = task;
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -411,25 +416,25 @@
Slog.e(TAG, "Can't get children of " + parent + " because it is not valid.");
return null;
}
- // For now, only support returning children of persistent root tasks (of which the
- // only current implementation is TaskTile).
- if (!(container instanceof TaskTile)) {
+ final Task task = container.asTask();
+ if (task == null) {
+ Slog.e(TAG, container + " is not a task...");
+ return null;
+ }
+ // For now, only support returning children of tasks created by the organizer.
+ if (!task.mCreatedByOrganizer) {
Slog.w(TAG, "Can only get children of root tasks created via createRootTask");
return null;
}
ArrayList<RunningTaskInfo> out = new ArrayList<>();
- // Tiles aren't real parents, so we need to go through stacks on the display to
- // ensure correct ordering.
- final DisplayContent dc = container.getDisplayContent();
- for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final ActivityStack as = dc.getStackAt(i);
- if (as.getTile() == container) {
- if (activityTypes != null
- && !ArrayUtils.contains(activityTypes, as.getActivityType())) {
- continue;
- }
- out.add(as.getTaskInfo());
+ for (int i = task.getChildCount() - 1; i >= 0; --i) {
+ final Task child = task.getChildAt(i).asTask();
+ if (child == null) continue;
+ if (activityTypes != null
+ && !ArrayUtils.contains(activityTypes, child.getActivityType())) {
+ continue;
}
+ out.add(child.getTaskInfo());
}
return out;
}
@@ -451,12 +456,7 @@
}
ArrayList<RunningTaskInfo> out = new ArrayList<>();
for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final ActivityStack task = dc.getStackAt(i);
- if (task.getTile() != null) {
- // a tile is supposed to look like a parent, so don't include their
- // "children" here. They can be accessed via getChildTasks()
- continue;
- }
+ final Task task = dc.getStackAt(i);
if (activityTypes != null
&& !ArrayUtils.contains(activityTypes, task.getActivityType())) {
continue;
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
deleted file mode 100644
index 51142b1..0000000
--- a/services/core/java/com/android/server/wm/TaskTile.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-
-import android.app.ActivityManager;
-import android.app.TaskInfo;
-import android.app.WindowConfiguration;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.function.Consumer;
-
-/**
- * A Tile. Right now this acts as a proxy for manipulating non-child stacks. Eventually, this
- * can become an actual parent.
- */
-// TODO(task-hierarchy): Remove when tasks can nest >2 or when single tasks can handle their
-// own lifecycles.
-public class TaskTile extends ActivityStack {
- private static final String TAG = "TaskTile";
- final ArrayList<WindowContainer> mChildren = new ArrayList<>();
-
- private static ActivityInfo createEmptyActivityInfo() {
- ActivityInfo info = new ActivityInfo();
- info.applicationInfo = new ApplicationInfo();
- return info;
- }
-
- TaskTile(ActivityTaskManagerService atmService, int id, int windowingMode) {
- super(atmService, id, new Intent() /*intent*/, null /*affinityIntent*/, null /*affinity*/,
- null /*rootAffinity*/, null /*realActivity*/, null /*origActivity*/,
- false /*rootWasReset*/, false /*autoRemoveRecents*/, false /*askedCompatMode*/,
- 0 /*userId*/, 0 /*effectiveUid*/, null /*lastDescription*/,
- System.currentTimeMillis(), true /*neverRelinquishIdentity*/,
- new ActivityManager.TaskDescription(), id, INVALID_TASK_ID, INVALID_TASK_ID,
- 0 /*taskAffiliationColor*/, 0 /*callingUid*/, "" /*callingPackage*/,
- null /*callingFeatureId*/, RESIZE_MODE_RESIZEABLE,
- false /*supportsPictureInPicture*/, false /*_realActivitySuspended*/,
- false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE,
- createEmptyActivityInfo(), null /*voiceSession*/, null /*voiceInteractor*/,
- null /*stack*/);
- getRequestedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
- }
-
- @Override
- void onDisplayChanged(DisplayContent dc) {
- mDisplayContent = null;
- if (dc != null) {
- dc.getPendingTransaction().merge(getPendingTransaction());
- }
- mDisplayContent = dc;
- // Virtual parent, so don't notify children.
- }
-
- @Override
- TaskTile asTile() {
- return this;
- }
-
- @Override
- protected void addChild(WindowContainer child, Comparator<WindowContainer> comparator) {
- throw new RuntimeException("Improper use of addChild() on Tile");
- }
-
- @Override
- void addChild(WindowContainer child, int index) {
- mChildren.add(child);
- if (child instanceof ActivityStack) {
- ((ActivityStack) child).setTile(this);
- }
- mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
- this, false /* force */);
- }
-
- @Override
- void removeChild(WindowContainer child) {
- if (child instanceof ActivityStack) {
- ((ActivityStack) child).setTile(null);
- }
- mChildren.remove(child);
- mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
- this, false /* force */);
- }
-
- void removeAllChildren() {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowContainer child = mChildren.get(i);
- if (child instanceof ActivityStack) {
- ((ActivityStack) child).setTile(null);
- }
- }
- mChildren.clear();
- mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
- this, false /* force */);
- }
-
- @Override
- protected int getChildCount() {
- // Currently 0 as this isn't a proper hierarchy member yet.
- return 0;
- }
-
- @Override
- public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
- Configuration c = new Configuration(getRequestedOverrideConfiguration());
- c.windowConfiguration.setWindowingMode(windowingMode);
- onRequestedOverrideConfigurationChanged(c);
- }
-
- @Override
- public void onConfigurationChanged(Configuration newParentConfig) {
- super.onConfigurationChanged(newParentConfig);
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowContainer child = mChildren.get(i);
- child.onConfigurationChanged(child.getParent().getConfiguration());
- }
- }
-
- void forAllTileActivities(Consumer<ActivityRecord> callback) {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- mChildren.get(i).forAllActivities(callback, true /* traverseTopToBottom */);
- }
- }
-
- /**
- * Until this can be part of the hierarchy, the Stack level can use this utility during
- * resolveOverrideConfig to simulate inheritance.
- */
- void updateResolvedConfig(Configuration inOutResolvedConfig) {
- Rect resolveBounds = inOutResolvedConfig.windowConfiguration.getBounds();
- if (resolveBounds.isEmpty()) {
- resolveBounds.set(getRequestedOverrideBounds());
- }
- int stackMode = inOutResolvedConfig.windowConfiguration.getWindowingMode();
- if (stackMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
- || stackMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN) {
- // Also replace FULLSCREEN because we interpret FULLSCREEN as "fill parent"
- inOutResolvedConfig.windowConfiguration.setWindowingMode(
- getRequestedOverrideWindowingMode());
- }
- if (inOutResolvedConfig.smallestScreenWidthDp
- == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
- inOutResolvedConfig.smallestScreenWidthDp =
- getRequestedOverrideConfiguration().smallestScreenWidthDp;
- }
- if (inOutResolvedConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- inOutResolvedConfig.screenWidthDp = getRequestedOverrideConfiguration().screenWidthDp;
- }
- if (inOutResolvedConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- inOutResolvedConfig.screenHeightDp = getRequestedOverrideConfiguration().screenHeightDp;
- }
- Rect resolveAppBounds = inOutResolvedConfig.windowConfiguration.getAppBounds();
- if (resolveAppBounds == null || resolveAppBounds.isEmpty()) {
- inOutResolvedConfig.windowConfiguration.setAppBounds(
- getRequestedOverrideConfiguration().windowConfiguration.getAppBounds());
- }
- }
-
- @Override
- void fillTaskInfo(TaskInfo info) {
- super.fillTaskInfo(info);
- WindowContainer top = null;
- // Check mChildren.isEmpty directly because hasChild() -> getChildCount() always returns 0
- if (!mChildren.isEmpty()) {
- // Find the top-most root task which is a virtual child of this Tile. Because this is a
- // virtual parent, the mChildren order here isn't changed during hierarchy operations.
- WindowContainer parent = mChildren.get(0).getParent();
- for (int i = parent.getChildCount() - 1; i >= 0; --i) {
- if (mChildren.contains(parent.getChildAt(i))) {
- top = parent.getChildAt(i);
- break;
- }
- }
- }
- final Task topTask = top == null ? null : top.getTopMostTask();
- boolean isResizable = topTask == null || topTask.isResizeable();
- info.resizeMode = isResizable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
- info.topActivityType = top == null ? ACTIVITY_TYPE_UNDEFINED : top.getActivityType();
- }
-
- @Override
- void removeImmediately() {
- removeAllChildren();
- super.removeImmediately();
- }
-
- @Override
- void taskOrganizerDied() {
- super.taskOrganizerDied();
- removeImmediately();
- }
-
- static TaskTile forToken(IBinder token) {
- try {
- return (TaskTile) ((RemoteToken) token).getContainer();
- } catch (ClassCastException e) {
- Slog.w(TAG, "Bad tile token: " + token, e);
- return null;
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index e416e80..5f21e17 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -159,17 +159,7 @@
false /* preserveWindow */);
try {
for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
- final WindowContainer wc = haveConfigChanges.valueAt(i);
- final Task task = wc.asTask();
- final TaskTile tile = task != null ? task.asTile() : null;
- if (tile != null) {
- // Special case for tile. Can't override normal forAllActivities
- // because it generates duplicate calls and messes up existing
- // code-paths.
- tile.forAllTileActivities(f);
- } else {
- wc.forAllActivities(f);
- }
+ haveConfigChanges.valueAt(i).forAllActivities(f);
}
} finally {
f.recycle();
@@ -223,51 +213,65 @@
private int sanitizeAndApplyHierarchyOp(WindowContainer container,
WindowContainerTransaction.HierarchyOp hop) {
- if (!(container instanceof Task)) {
+ final Task task = container.asTask();
+ if (task == null) {
throw new IllegalArgumentException("Invalid container in hierarchy op");
}
- if (container.getDisplayContent() == null) {
- Slog.w(TAG, "Container is no longer attached: " + container);
+ final DisplayContent dc = task.getDisplayContent();
+ if (dc == null) {
+ Slog.w(TAG, "Container is no longer attached: " + task);
return 0;
}
+ final ActivityStack as = (ActivityStack) task;
+
if (hop.isReparent()) {
- // special case for tiles since they are "virtual" parents
- if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
- ActivityStack as = (ActivityStack) container;
- TaskTile newParent = hop.getNewParent() == null ? null
- : (TaskTile) WindowContainer.fromBinder(hop.getNewParent());
- if (as.getTile() != newParent) {
- if (as.getTile() != null) {
- as.getTile().removeChild(as);
+ final boolean isNonOrganizedRootableTask =
+ (task.isRootTask() && !task.mCreatedByOrganizer)
+ || task.getParent().asTask().mCreatedByOrganizer;
+ if (isNonOrganizedRootableTask) {
+ Task newParent = hop.getNewParent() == null ? null
+ : WindowContainer.fromBinder(hop.getNewParent()).asTask();
+ if (task.getParent() != newParent) {
+ if (newParent == null) {
+ // Re-parent task to display as a root task.
+ dc.moveStackToDisplay(as, hop.getToTop());
+ } else if (newParent.inMultiWindowMode() && !task.isResizeable()
+ && task.isLeafTask()) {
+ Slog.w(TAG, "Can't support task that doesn't support multi-window mode in"
+ + " multi-window mode... newParent=" + newParent + " task=" + task);
+ return 0;
+ } else {
+ // Clear the window crop on root task since it may not be updated after
+ // reparent (no longer be a root task)
+ task.getSurfaceControl().setWindowCrop(null);
+ task.reparent((ActivityStack) newParent,
+ hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+ false /*moveParents*/, "sanitizeAndApplyHierarchyOp");
}
- if (newParent != null) {
- if (!as.affectedBySplitScreenResize()) {
- return 0;
- }
- newParent.addChild(as, POSITION_TOP);
- }
- }
- if (hop.getToTop()) {
- as.getDisplay().positionStackAtTop(as, false /* includingParents */);
} else {
- as.getDisplay().positionStackAtBottom(as);
+ final ActivityStack rootTask =
+ (ActivityStack) (newParent != null ? newParent : task.getRootTask());
+ if (hop.getToTop()) {
+ as.getDisplay().positionStackAtTop(rootTask, false /* includingParents */);
+ } else {
+ as.getDisplay().positionStackAtBottom(rootTask);
+ }
}
- } else if (container instanceof Task) {
+ } else {
throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
}
} else {
// Ugh, of course ActivityStack has its own special reorder logic...
- if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
- ActivityStack as = (ActivityStack) container;
+ if (task.isRootTask()) {
if (hop.getToTop()) {
- as.getDisplay().positionStackAtTop(as, false /* includingParents */);
+ dc.positionStackAtTop(as, false /* includingParents */);
} else {
- as.getDisplay().positionStackAtBottom(as);
+ dc.positionStackAtBottom(as);
}
} else {
- container.getParent().positionChildAt(
+ task.getParent().positionChildAt(
hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
- container, false /* includingParents */);
+ task, false /* includingParents */);
}
}
return TRANSACT_EFFECTS_LIFECYCLE;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 161152b..0338cc3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -131,6 +131,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
@@ -138,7 +139,6 @@
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
-import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
@@ -3338,7 +3338,7 @@
}
final ActivityStack stack = task.getStack();
- if (stack == null) {
+ if (stack == null || stack.mCreatedByOrganizer) {
return;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 023a1e8..09fab3e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4434,6 +4434,11 @@
clearDeviceOwnerLocked(getDeviceOwnerAdminLocked(), userHandle);
}
if (isProfileOwner(adminReceiver, userHandle)) {
+ if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+ false,
+ UserHandle.of(getProfileParentId(userHandle)));
+ }
final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver,
userHandle, /* parent */ false);
clearProfileOwnerLocked(admin, userHandle);
@@ -13070,26 +13075,25 @@
final boolean addingProfileRestricted = mUserManager.hasUserRestriction(
UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle);
- UserInfo parentUser = mUserManager.getProfileParent(callingUserId);
- final boolean addingProfileRestrictedOnParent = (parentUser != null)
- && mUserManager.hasUserRestriction(
- UserManager.DISALLOW_ADD_MANAGED_PROFILE,
- UserHandle.of(parentUser.id));
+ if (mUserManager.getUserInfo(callingUserId).isProfile()) {
+ Slog.i(LOG_TAG,
+ String.format("Calling user %d is a profile, cannot add another.",
+ callingUserId));
+ // The check is called from inside a managed profile. A managed profile cannot
+ // be provisioned from within another managed profile.
+ return CODE_CANNOT_ADD_MANAGED_PROFILE;
+ }
- Slog.i(LOG_TAG, String.format(
- "When checking for managed profile provisioning: Has device owner? %b, adding"
- + " profile restricted? %b, adding profile restricted on parent? %b",
- hasDeviceOwner, addingProfileRestricted, addingProfileRestrictedOnParent));
-
- // If there's a device owner, the restriction on adding a managed profile must be set
- // somewhere.
- if (hasDeviceOwner && !addingProfileRestricted && !addingProfileRestrictedOnParent) {
+ // If there's a device owner, the restriction on adding a managed profile must be set.
+ if (hasDeviceOwner && !addingProfileRestricted) {
Slog.wtf(LOG_TAG, "Has a device owner but no restriction on adding a profile.");
}
- // Do not allow adding a managed profile if there's a restriction, either on the current
- // user or its parent user.
- if (addingProfileRestricted || addingProfileRestrictedOnParent) {
+ // Do not allow adding a managed profile if there's a restriction.
+ if (addingProfileRestricted) {
+ Slog.i(LOG_TAG, String.format(
+ "Adding a profile is restricted: User %s Has device owner? %b",
+ callingUserHandle, hasDeviceOwner));
return CODE_CANNOT_ADD_MANAGED_PROFILE;
}
// If there's a restriction on removing the managed profile then we have to take it
@@ -13098,6 +13102,8 @@
!mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
callingUserHandle);
if (!mUserManager.canAddMoreManagedProfiles(callingUserId, canRemoveProfile)) {
+ Slog.i(LOG_TAG, String.format(
+ "Cannot add more profiles: Can remove current? %b", canRemoveProfile));
return CODE_CANNOT_ADD_MANAGED_PROFILE;
}
} finally {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 7c6ac17..baf551e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3205,6 +3205,7 @@
when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
.thenReturn(true);
setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
+ when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null);
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
}
@@ -3246,6 +3247,7 @@
when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
.thenReturn(true);
setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
+ when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null);
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
}
@@ -3617,14 +3619,14 @@
when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
.thenReturn(true);
- when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false);
when(getServices().userManager.getProfileParent(DpmMockContext.CALLER_USER_HANDLE))
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
when(getServices().userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
true)).thenReturn(true);
setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE);
- mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
}
public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_primaryUser()
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 6ec0f91..ed400ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -82,9 +82,8 @@
import android.platform.test.annotations.Presubmit;
import android.service.voice.IVoiceInteractionSession;
import android.view.Gravity;
-import android.window.IWindowContainer;
-import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
+import android.window.IWindowContainer;
import androidx.test.filters.SmallTest;
@@ -975,7 +974,7 @@
// Move activity to split-screen-primary stack and make sure it has the focus.
TestSplitOrganizer splitOrg = new TestSplitOrganizer(mService, top.getDisplayId());
- splitOrg.mPrimary.addChild(top.getRootTask(), 0 /* index */);
+ top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM);
top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent");
// Activity must landed on split-screen-secondary when launch adjacent.
@@ -1001,8 +1000,8 @@
static class TestSplitOrganizer extends ITaskOrganizer.Stub {
final ActivityTaskManagerService mService;
- TaskTile mPrimary;
- TaskTile mSecondary;
+ Task mPrimary;
+ Task mSecondary;
boolean mInSplit = false;
int mDisplayId;
TestSplitOrganizer(ActivityTaskManagerService service, int displayId) {
@@ -1014,10 +1013,10 @@
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
IWindowContainer primary = mService.mTaskOrganizerController.createRootTask(
displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token;
- mPrimary = TaskTile.forToken(primary.asBinder());
+ mPrimary = WindowContainer.fromBinder(primary.asBinder()).asTask();
IWindowContainer secondary = mService.mTaskOrganizerController.createRootTask(
displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token;
- mSecondary = TaskTile.forToken(secondary.asBinder());
+ mSecondary = WindowContainer.fromBinder(secondary.asBinder()).asTask();
}
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo info) {
@@ -1042,7 +1041,7 @@
for (int i = dc.getStackCount() - 1; i >= 0; --i) {
if (!WindowConfiguration.isSplitScreenWindowingMode(
dc.getStackAt(i).getWindowingMode())) {
- mSecondary.addChild(dc.getStackAt(i), 0);
+ dc.getStackAt(i).reparent(mSecondary, POSITION_BOTTOM);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 4634e2d..716369d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -464,7 +464,7 @@
ActivityStack build() {
final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
final ActivityStack stack = mDisplay.createStackUnchecked(mWindowingMode,
- mActivityType, stackId, mOnTop, mInfo, mIntent);
+ mActivityType, stackId, mOnTop, mInfo, mIntent, false /* createdByOrganizer */);
final ActivityStackSupervisor supervisor = mRootWindowContainer.mStackSupervisor;
if (mCreateActivity) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index a96f401..ed635ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -37,6 +37,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -347,45 +348,62 @@
assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType);
DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
- List<TaskTile> infos = getTaskTiles(dc);
+ List<Task> infos = getTasksCreatedByOrganizer(dc);
assertEquals(2, infos.size());
assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token));
- infos = getTaskTiles(dc);
+ infos = getTasksCreatedByOrganizer(dc);
assertEquals(1, infos.size());
assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, infos.get(0).getWindowingMode());
}
@Test
public void testTileAddRemoveChild() {
+ ITaskOrganizer listener = new ITaskOrganizer.Stub() {
+ @Override
+ public void onTaskAppeared(RunningTaskInfo taskInfo) { }
+
+ @Override
+ public void onTaskVanished(RunningTaskInfo container) { }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo info) throws RemoteException {
+ }
+ };
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
final ActivityStack stack = createTaskStackOnDisplay(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
- TaskTile tile1 = TaskTile.forToken(info1.token.asBinder());
- tile1.addChild(stack, 0 /* index */);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertEquals(info1.configuration.windowConfiguration.getWindowingMode(),
stack.getWindowingMode());
// Info should reflect new membership
- List<TaskTile> tiles = getTaskTiles(mDisplayContent);
- info1 = tiles.get(0).getTaskInfo();
+ List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent);
+ info1 = infos.get(0).getTaskInfo();
assertEquals(ACTIVITY_TYPE_STANDARD, info1.topActivityType);
// Children inherit configuration
Rect newSize = new Rect(10, 10, 300, 300);
- Configuration c = new Configuration(tile1.getRequestedOverrideConfiguration());
+ Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
+ Configuration c = new Configuration(task1.getRequestedOverrideConfiguration());
c.windowConfiguration.setBounds(newSize);
doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any());
- tile1.onRequestedOverrideConfigurationChanged(c);
+ task1.onRequestedOverrideConfigurationChanged(c);
assertEquals(newSize, stack.getBounds());
- tile1.removeChild(stack);
+ wct = new WindowContainerTransaction();
+ wct.reparent(stack.mRemoteToken, null, true /* onTop */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
- tiles = getTaskTiles(mDisplayContent);
- info1 = tiles.get(0).getTaskInfo();
+ infos = getTasksCreatedByOrganizer(mDisplayContent);
+ info1 = infos.get(0).getTaskInfo();
assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
}
@@ -415,8 +433,10 @@
final ActivityStack stack = createTaskStackOnDisplay(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
- TaskTile tile1 = TaskTile.forToken(info1.token.asBinder());
- tile1.addChild(stack, 0 /* index */);
+ Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertTrue(called[0]);
assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
@@ -424,19 +444,24 @@
called[0] = false;
final ActivityStack stack2 = createTaskStackOnDisplay(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
- tile1.addChild(stack2, 0 /* index */);
+ wct = new WindowContainerTransaction();
+ wct.reparent(stack2.mRemoteToken, info1.token, true /* onTop */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertTrue(called[0]);
assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType);
lastReportedTiles.clear();
called[0] = false;
- mDisplayContent.positionStackAtTop(stack, false /* includingParents */);
+ task1.positionChildAt(POSITION_TOP, stack, false /* includingParents */);
assertTrue(called[0]);
assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
lastReportedTiles.clear();
called[0] = false;
- tile1.removeAllChildren();
+ wct = new WindowContainerTransaction();
+ wct.reparent(stack.mRemoteToken, null, true /* onTop */);
+ wct.reparent(stack2.mRemoteToken, null, true /* onTop */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertTrue(called[0]);
assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType);
}
@@ -457,9 +482,11 @@
}
};
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
+ listener, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
listener, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
@@ -522,13 +549,11 @@
lastReportedTiles.get(info1.token.asBinder()).topActivityType);
}
- private List<TaskTile> getTaskTiles(DisplayContent dc) {
- ArrayList<TaskTile> out = new ArrayList<>();
+ private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) {
+ ArrayList<Task> out = new ArrayList<>();
for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final TaskTile t = dc.getStackAt(i).asTile();
- if (t != null) {
- out.add(t);
- }
+ final Task t = dc.getStackAt(i);
+ if (t.mCreatedByOrganizer) out.add(t);
}
return out;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 5848295..56c19a4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -152,7 +152,7 @@
assertFalse(factory.mCreated);
Task.create(mService, 0 /*taskId*/, 0 /*activityType*/,
- new ActivityInfo(), new Intent());
+ new ActivityInfo(), new Intent(), false /* createdByOrganizer */);
assertTrue(factory.mCreated);
} finally {
@@ -1015,7 +1015,7 @@
@Override
Task create(ActivityTaskManagerService service, int taskId, int activityType,
- ActivityInfo info, Intent intent) {
+ ActivityInfo info, Intent intent, boolean createdByOrganizer) {
mCreated = true;
return null;
}
diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index 269cec1..7b85cc8 100644
--- a/tests/RollbackTest/RollbackTest.xml
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -23,6 +23,10 @@
<option name="run-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es package "com.google.android.gms.platformconfigurator" --es user '\\*' --esa flags "ModuleConfig__versioned_immediate_commit_packages" --esa types "bytes" --esa values "Cm5vdGFwYWNrYWdlOgA=" com.google.android.gms" />
<option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package "com.google.android.gms.platformconfigurator" --es user '\*' --esa flag "ModuleConfig__immediate_commit_packages" com.google.android.gms" />
<option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package "com.google.android.gms.platformconfigurator" --es user '\*' --esa flag "ModuleConfig__versioned_immediate_commit_packages" com.google.android.gms" />
+ <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+ <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.tests.rollback" />
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 5a92d68..cab8b42 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -75,6 +75,12 @@
private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS =
"enable_rollback_timeout";
+ private static boolean hasRollbackInclude(List<RollbackInfo> rollbacks, String packageName) {
+ return rollbacks.stream().anyMatch(
+ ri -> ri.getPackages().stream().anyMatch(
+ pri -> packageName.equals(pri.getPackageName())));
+ }
+
/**
* Test basic rollbacks.
*/
@@ -113,18 +119,14 @@
// Uninstall TestApp.A
Uninstall.packages(TestApp.A);
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
- // TODO: There is currently a race condition between when the app is
- // uninstalled and when rollback manager deletes the rollback. Fix it
- // so that's not the case!
for (int i = 0; i < 5; ++i) {
- RollbackInfo rollback = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
- if (rollback != null) {
+ if (hasRollbackInclude(rm.getRecentlyCommittedRollbacks(), TestApp.A)) {
Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
Thread.sleep(1000);
}
}
+ assertThat(hasRollbackInclude(rm.getRecentlyCommittedRollbacks(), TestApp.A)).isFalse();
// The app should not be available for rollback.
waitForUnavailableRollback(TestApp.A);
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 9d9a495..d0f1a26 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -170,24 +170,23 @@
java_library {
name: "framework-wifi-stubs-publicapi",
srcs: [":framework-wifi-stubs-srcs-publicapi"],
+ defaults: ["framework-module-stubs-lib-defaults-publicapi"],
+ // TODO(b/151134996): remove this
sdk_version: "current",
- installable: false,
}
java_library {
name: "framework-wifi-stubs-systemapi",
srcs: [":framework-wifi-stubs-srcs-systemapi"],
- sdk_version: "system_current",
libs: ["framework-annotations-lib"],
- installable: false,
+ defaults: ["framework-module-stubs-lib-defaults-systemapi"],
}
java_library {
name: "framework-wifi-stubs-module_libs_api",
srcs: [":framework-wifi-stubs-srcs-module_libs_api"],
- sdk_version: "module_current",
libs: ["framework-annotations-lib"],
- installable: false,
+ defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
}
// defaults for tests that need to build against framework-wifi's @hide APIs