build/make
d3d00079d8: Version bump to AP3A.241105.007 [core/build_id.mk] (Android Build Coastguard Worker <android-build-c...)
c3c41f4a31: Version bump to AP3A.241105.006 [core/build_id.mk] (Android Build Coastguard Worker <android-build-c...)
f0601c094a: Move ap3a configuration to build/release. (Ian Kasprzak <[email protected]>)
32a7ed5769: Version bump to AP3A.241105.004 [core/build_id.mk] (Android Build Coastguard Worker <android-build-c...)
bb03ce0557: Version bump to AP3A.241105.003 [core/build_id.mk] (Android Build Coastguard Worker <android-build-c...)
f8bbbe0cbd: Version bump to AP3A.241105.002.X1 [core/build_id.mk] (Android Build Coastguard Worker <android-build-c...)
390c65a255: Version bump to AP3A.241105.002 [core/build_id.mk] (Android Build Coastguard Worker <android-build-c...)
c12eed7b94: Version bump to AP3A.241105.001 [core/build_id.mk] (Android Build Coastguard Worker <android-build-c...)
b37813e557: Version bump to AP3A.241005.016 [core/build_id.mk] (Android Build Coastguard Worker <android-build-c...)
build/release
0d37102: AP3A: Set SPL to 2024-11-05 (Ankur Bakshi <[email protected]>)
2654c89: Update aconfig flags for ap3a and trunk_staging to (ab/AP3A.241105.008). (Bill Yi <[email protected]>)
cts
389c25f21ed: Disable ASM_RESTRICTIONS flag (Hani Kazmi <[email protected]>)
device/google/gs-common
d93d355: bootctrl: fixed OOB read in BootControl (bgkim <[email protected]>)
external/skia
9adb3df618: Avoid potential overflow when allocating 3D mask from emboss filter (Nolan Scobie <[email protected]>)
frameworks/base
5e4364e48b9b: Disable ASM_RESTRICTIONS flag (Hani Kazmi <[email protected]>)
61dbf0458ca1: Allows an app to cancel a device state request they made at any point if... (Kenneth Ford <[email protected]>)
f5f5eb6965b7: Don't send BOOT_COMPLETED to app in restricted backup mode (Amith Yamasani <[email protected]>)
1f1204554833: Add notification for onInferenceServiceDisconnected (sandeepbandaru <[email protected]>)
650f04532d84: Remove dumpmanager from KeyguardStatusView (Matt Pietal <[email protected]>)
a39b9a800884: Fix regression in getting top running task to split with (Winson Chung <[email protected]>)
7db0a1156449: Fix issue where highlight was drawing without bottom inset on one side o... (Winson Chung <[email protected]>)
f94e099ed8d6: Add mechanism for a task to be hidden as a part of starting a drag (Winson Chung <[email protected]>)
4a78cc849725: Fix issue with incorrect drag starter being used and handle removed task... (Winson Chung <[email protected]>)
68eb5dc1200f: Restrict access to directories (Dipankar Bhardwaj <[email protected]>)
706714b827fb: Disallow device admin package and protected packages to be reinstalled a... (lpeter <[email protected]>)
fc51b1be63dd: Set no data transfer on function switch timeout for accessory mode (Ashish Kumar Gupta <[email protected]>)
bd19f063982c: Remove authenticator data if it was disabled. (Dmitry Dementyev <[email protected]>)
frameworks/native
a69ae94ac6: Fix DisplayState sanitization. (Patrick Williams <[email protected]>)
frameworks/opt/telephony
6169691f29: Revert "If the phone crash try to clean up the channel which was kept op... (Aman Gupta <[email protected]>)
5abd191dde: Revert "If the phone crash try to clean up the channel which was kept op... (Rambo Wang <[email protected]>)
hardware/google/graphics/common
99e8608: libhwc2.1: display temperature in scene (Peter Lin <[email protected]>)
4f2d23c: libhwc2.1: add monitor display thermal temperature (gilliu <[email protected]>)
hardware/google/graphics/gs101
ccd4a23: libhwc2.1: apply the display temperatue to display scene (Peter Lin <[email protected]>)
manifest
ba7d47699: Manifest for Android 15.0.0 Release 4 (The Android Open Source Project <initial-contrib...)
packages/apps/Settings
7c2681d92ad: Stops hiding a11y services with the same package+label as an activity. (Daniel Norman <[email protected]>)
dcf8f310eef: Only check INTERACT_ACROSS_USERS_FULL when user handle is not current (Chris Antol <[email protected]>)
b86100f3541: Checks cross user permission before handling intent (Fan Wu <[email protected]>)
8c8d3eac3bd: startActivityForResult with new Intent (Adam Bookatz <[email protected]>)
packages/modules/Bluetooth
89d2b52bed: Interop fix to not read PPCP for devices that report incompatible values... (Omair Kamil <[email protected]>)
packages/modules/Wifi
e59d5991b9: Fix security isseu by change the field in WifiConfig (Nate Jiang <[email protected]>)
packages/providers/MediaProvider
64b0bb2dc: Prevent apps from renaming files they don't own (Omar Eissa <[email protected]>)
system/core
689516ac96: libsnapshot: Address GRF config when updating from Android S config (Akilesh Kailash <[email protected]>)
93096918b1: libsnapshot: Check if the vendor is updated from Android S for GRF (Akilesh Kailash <[email protected]>)
build/make
diff --git a/core/build_id.mk b/core/build_id.mk
index c68fc2387f..015f677cd5 100644
--- a/core/build_id.mk
+++ b/core/build_id.mk
@@ -18,4 +18,4 @@
# (like "CRB01"). It must be a single word, and is
# capitalized by convention.
-BUILD_ID=AP3A.241005.015.A2
+BUILD_ID=AP3A.241105.007
diff --git a/target/product/generic_system.mk b/target/product/generic_system.mk
index 0a09eb11d4..b9a623dcd3 100644
--- a/target/product/generic_system.mk
+++ b/target/product/generic_system.mk
@@ -152,4 +152,5 @@ _my_paths := \
$(call require-artifacts-in-path, $(_my_paths), $(_my_allowed_list))
# Product config map to toggle between sources and prebuilts of required mainline modules
+PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard build/release/gms_mainline/required/release_config_map.textproto)
PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard vendor/google_shared/build/release/gms_mainline/required/release_config_map.textproto)
diff --git a/target/product/go_defaults.mk b/target/product/go_defaults.mk
index dd6a955ce7..6ee8fb415b 100644
--- a/target/product/go_defaults.mk
+++ b/target/product/go_defaults.mk
@@ -18,6 +18,7 @@
$(call inherit-product, build/make/target/product/go_defaults_common.mk)
# Product config map to toggle between sources and prebuilts of required mainline modules
+PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard build/release/gms_mainline_go/required/release_config_map.textproto)
PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard vendor/google_shared/build/release/gms_mainline_go/required/release_config_map.textproto)
# TODO (b/342265627): Remove v/g/r once all the flags have been moved to v/g_s/b/r
build/release
diff --git a/aconfig/ap3a/android.hardware.devicestate.feature.flags/device_state_requester_cancel_state_flag_values.textproto b/aconfig/ap3a/android.hardware.devicestate.feature.flags/device_state_requester_cancel_state_flag_values.textproto
new file mode 100644
index 0000000..3e1e4a7
--- /dev/null
+++ b/aconfig/ap3a/android.hardware.devicestate.feature.flags/device_state_requester_cancel_state_flag_values.textproto
@@ -0,0 +1,6 @@
+flag_value {
+ package: "android.hardware.devicestate.feature.flags"
+ name: "device_state_requester_cancel_state"
+ state: ENABLED
+ permission: READ_ONLY
+}
\ No newline at end of file
diff --git a/flag_values/ap3a/RELEASE_KERNEL_AKITA_DIR.textproto b/flag_values/ap3a/RELEASE_KERNEL_AKITA_DIR.textproto
index d6253e6..3da5f42 100644
--- a/flag_values/ap3a/RELEASE_KERNEL_AKITA_DIR.textproto
+++ b/flag_values/ap3a/RELEASE_KERNEL_AKITA_DIR.textproto
@@ -1,4 +1,4 @@
name: "RELEASE_KERNEL_AKITA_DIR"
value: {
- string_value: "device/google/akita-kernels/5.15/24Q3-12065098"
+ string_value: "device/google/akita-kernels/5.15/24Q3-12357444"
}
diff --git a/flag_values/ap3a/RELEASE_KERNEL_BLUEJAY_DIR.textproto b/flag_values/ap3a/RELEASE_KERNEL_BLUEJAY_DIR.textproto
index 06f2752..df642b9 100644
--- a/flag_values/ap3a/RELEASE_KERNEL_BLUEJAY_DIR.textproto
+++ b/flag_values/ap3a/RELEASE_KERNEL_BLUEJAY_DIR.textproto
@@ -1,4 +1,4 @@
name: "RELEASE_KERNEL_BLUEJAY_DIR"
value: {
- string_value: "device/google/bluejay-kernels/5.10/24Q3-12115410"
+ string_value: "device/google/bluejay-kernels/5.10/24Q3-12357445"
}
diff --git a/flag_values/ap3a/RELEASE_KERNEL_HUSKY_DIR.textproto b/flag_values/ap3a/RELEASE_KERNEL_HUSKY_DIR.textproto
index 03b8e2f..f4de003 100644
--- a/flag_values/ap3a/RELEASE_KERNEL_HUSKY_DIR.textproto
+++ b/flag_values/ap3a/RELEASE_KERNEL_HUSKY_DIR.textproto
@@ -1,4 +1,4 @@
name: "RELEASE_KERNEL_HUSKY_DIR"
value: {
- string_value: "device/google/shusky-kernels/5.15/24Q3-12065098"
+ string_value: "device/google/shusky-kernels/5.15/24Q3-12357444"
}
diff --git a/flag_values/ap3a/RELEASE_KERNEL_ORIOLE_DIR.textproto b/flag_values/ap3a/RELEASE_KERNEL_ORIOLE_DIR.textproto
index 5b1cfb4..0ce36ae 100644
--- a/flag_values/ap3a/RELEASE_KERNEL_ORIOLE_DIR.textproto
+++ b/flag_values/ap3a/RELEASE_KERNEL_ORIOLE_DIR.textproto
@@ -1,4 +1,4 @@
name: "RELEASE_KERNEL_ORIOLE_DIR"
value: {
- string_value: "device/google/raviole-kernels/5.10/24Q3-12115410"
+ string_value: "device/google/raviole-kernels/5.10/24Q3-12357445"
}
diff --git a/flag_values/ap3a/RELEASE_KERNEL_RAVEN_DIR.textproto b/flag_values/ap3a/RELEASE_KERNEL_RAVEN_DIR.textproto
index bb9b33b..11bab4d 100644
--- a/flag_values/ap3a/RELEASE_KERNEL_RAVEN_DIR.textproto
+++ b/flag_values/ap3a/RELEASE_KERNEL_RAVEN_DIR.textproto
@@ -1,4 +1,4 @@
name: "RELEASE_KERNEL_RAVEN_DIR"
value: {
- string_value: "device/google/raviole-kernels/5.10/24Q3-12115410"
+ string_value: "device/google/raviole-kernels/5.10/24Q3-12357445"
}
diff --git a/flag_values/ap3a/RELEASE_KERNEL_SHIBA_DIR.textproto b/flag_values/ap3a/RELEASE_KERNEL_SHIBA_DIR.textproto
index edac628..add95e7 100644
--- a/flag_values/ap3a/RELEASE_KERNEL_SHIBA_DIR.textproto
+++ b/flag_values/ap3a/RELEASE_KERNEL_SHIBA_DIR.textproto
@@ -1,4 +1,4 @@
name: "RELEASE_KERNEL_SHIBA_DIR"
value: {
- string_value: "device/google/shusky-kernels/5.15/24Q3-12065098"
+ string_value: "device/google/shusky-kernels/5.15/24Q3-12357444"
}
diff --git a/flag_values/ap3a/RELEASE_PLATFORM_SECURITY_PATCH.textproto b/flag_values/ap3a/RELEASE_PLATFORM_SECURITY_PATCH.textproto
index f27e557..ce29523 100644
--- a/flag_values/ap3a/RELEASE_PLATFORM_SECURITY_PATCH.textproto
+++ b/flag_values/ap3a/RELEASE_PLATFORM_SECURITY_PATCH.textproto
@@ -1,4 +1,4 @@
name: "RELEASE_PLATFORM_SECURITY_PATCH"
value: {
- string_value: "2024-10-05"
+ string_value: "2024-11-05"
}
cts
diff --git a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/ActivitySecurityModelEmbeddingTest.java b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/ActivitySecurityModelEmbeddingTest.java
index 85d1cbaccdb..378a78728e0 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/ActivitySecurityModelEmbeddingTest.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/ActivitySecurityModelEmbeddingTest.java
@@ -23,8 +23,10 @@ import android.content.ComponentName;
import androidx.test.filters.FlakyTest;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
+@Ignore
public class ActivitySecurityModelEmbeddingTest extends BackgroundActivityTestBase {
@Override
diff --git a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/ActivitySecurityModelTest.java b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/ActivitySecurityModelTest.java
index 9593dadd5a6..f181d7db923 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/ActivitySecurityModelTest.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/ActivitySecurityModelTest.java
@@ -24,9 +24,11 @@ import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.security.Flags;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
+@Ignore
public class ActivitySecurityModelTest extends BackgroundActivityTestBase {
@Rule
public final CheckFlagsRule mCheckFlagsRule =
diff --git a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
index 42a9237bd6a..c71955b6e70 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
@@ -194,6 +194,7 @@ public class BackgroundActivityLaunchTest extends BackgroundActivityTestBase {
}
@Test
+ @Ignore
public void testBackgroundActivity_withinASMGracePeriod_isBlocked() throws Exception {
assumeSdkNewerThanUpsideDownCake();
// Start AppA foreground activity
@@ -207,6 +208,7 @@ public class BackgroundActivityLaunchTest extends BackgroundActivityTestBase {
@Test
@FlakyTest(bugId = 297339382)
+ @Ignore
public void testBackgroundActivity_withinBalAfterAsmGracePeriod_isBlocked()
throws Exception {
assumeSdkNewerThanUpsideDownCake();
@@ -231,6 +233,7 @@ public class BackgroundActivityLaunchTest extends BackgroundActivityTestBase {
}
@Test
+ @Ignore
public void testBackgroundActivityBlockedWhenForegroundActivityNotTop() throws Exception {
assumeSdkNewerThanUpsideDownCake();
@@ -316,6 +319,7 @@ public class BackgroundActivityLaunchTest extends BackgroundActivityTestBase {
}
@Test
+ @Ignore
public void testActivityBlockedFromBgActivityInFgTask() {
assumeSdkNewerThanUpsideDownCake();
// Launch Activity A, B in the same task with different processes.
device/google/gs-common
diff --git a/bootctrl/aidl/BootControl.cpp b/bootctrl/aidl/BootControl.cpp
index e771845..d894f8b 100644
--- a/bootctrl/aidl/BootControl.cpp
+++ b/bootctrl/aidl/BootControl.cpp
@@ -384,7 +384,7 @@ ScopedAStatus BootControl::isSlotMarkedSuccessful(int32_t in_slot, bool* _aidl_r
*_aidl_return = true;
return ScopedAStatus::ok();
}
- if (in_slot >= slots)
+ if (in_slot < 0 || in_slot >= slots)
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
INVALID_SLOT, (std::string("Invalid slot ") + std::to_string(in_slot)).c_str());
external/skia
diff --git a/src/effects/SkEmbossMaskFilter.cpp b/src/effects/SkEmbossMaskFilter.cpp
index 3d431f812e..c8f0c536b3 100644
--- a/src/effects/SkEmbossMaskFilter.cpp
+++ b/src/effects/SkEmbossMaskFilter.cpp
@@ -99,11 +99,13 @@ bool SkEmbossMaskFilter::filterMask(SkMaskBuilder* dst, const SkMask& src,
{
uint8_t* alphaPlane = dst->image();
- size_t planeSize = dst->computeImageSize();
- if (0 == planeSize) {
- return false; // too big to allocate, abort
+ size_t totalSize = dst->computeTotalImageSize();
+ if (totalSize == 0) {
+ return false; // too big to allocate, abort
}
- dst->image() = SkMaskBuilder::AllocImage(planeSize * 3);
+ size_t planeSize = dst->computeImageSize();
+ SkASSERT(planeSize != 0); // if totalSize didn't overflow, this can't either
+ dst->image() = SkMaskBuilder::AllocImage(totalSize);
memcpy(dst->image(), alphaPlane, planeSize);
SkMaskBuilder::FreeImage(alphaPlane);
}
frameworks/base
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index 5953890ad85f..93724bb4949d 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -135,6 +135,14 @@ public class ClipDescription implements Parcelable {
public static final String EXTRA_LOGGING_INSTANCE_ID =
"android.intent.extra.LOGGING_INSTANCE_ID";
+ /**
+ * The id of the task containing the window that initiated the drag that should be hidden.
+ * Only provided to internal drag handlers as a part of the DRAG_START event.
+ * @hide
+ */
+ public static final String EXTRA_HIDE_DRAG_SOURCE_TASK_ID =
+ "android.intent.extra.HIDE_DRAG_SOURCE_TASK_ID";
+
/**
* Indicates that a ClipData contains potentially sensitive information, such as a
* password or credit card number.
diff --git a/core/java/android/hardware/devicestate/feature/flags.aconfig b/core/java/android/hardware/devicestate/feature/flags.aconfig
index 12d3f94ec982..7ffd26cd94f9 100644
--- a/core/java/android/hardware/devicestate/feature/flags.aconfig
+++ b/core/java/android/hardware/devicestate/feature/flags.aconfig
@@ -8,4 +8,16 @@ flag {
description: "Updated DeviceState hasProperty API"
bug: "293636629"
is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+ name: "device_state_requester_cancel_state"
+ is_exported: true
+ namespace: "windowing_sdk"
+ description: "Removes foreground requirement if process attempting to cancel a state request is the requester"
+ bug: "354772125"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9bc15112debc..2ae5a442355c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5511,6 +5511,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@FlaggedApi(FLAG_DELEGATE_UNHANDLED_DRAGS)
public static final int DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG = 1 << 13;
+ /**
+ * Flag indicating that this drag will result in the caller activity's task to be hidden for the
+ * duration of the drag, this means that the source activity will not receive drag events for
+ * the current drag gesture. Only the current voice interaction service may use this flag.
+ * @hide
+ */
+ public static final int DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START = 1 << 14;
+
/**
* Vertical scroll factor cached by {@link #getVerticalScrollFactor}.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 3ded7d246499..863186551f69 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -123,6 +123,15 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
default void dump(@NonNull PrintWriter pw, String prefix) {};
}
+ /**
+ * Limited scope callback to notify when a task is removed from the system. This signal is
+ * not synchronized with anything (or any transition), and should not be used in cases where
+ * that is necessary.
+ */
+ public interface TaskVanishedListener {
+ default void onTaskVanished(RunningTaskInfo taskInfo) {}
+ }
+
/**
* Callbacks for events on a task with a locus id.
*/
@@ -167,6 +176,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
private final ArraySet<FocusListener> mFocusListeners = new ArraySet<>();
+ // Listeners that should be notified when a task is removed
+ private final ArraySet<TaskVanishedListener> mTaskVanishedListeners = new ArraySet<>();
+
private final Object mLock = new Object();
private StartingWindowController mStartingWindow;
@@ -409,7 +421,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
/**
- * Removes listener.
+ * Removes a locus id listener.
*/
public void removeLocusIdListener(LocusIdListener listener) {
synchronized (mLock) {
@@ -430,7 +442,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
/**
- * Removes listener.
+ * Removes a focus listener.
*/
public void removeFocusListener(FocusListener listener) {
synchronized (mLock) {
@@ -438,6 +450,24 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
}
+ /**
+ * Adds a listener to be notified when a task vanishes.
+ */
+ public void addTaskVanishedListener(TaskVanishedListener listener) {
+ synchronized (mLock) {
+ mTaskVanishedListeners.add(listener);
+ }
+ }
+
+ /**
+ * Removes a task-vanished listener.
+ */
+ public void removeTaskVanishedListener(TaskVanishedListener listener) {
+ synchronized (mLock) {
+ mTaskVanishedListeners.remove(listener);
+ }
+ }
+
/**
* Returns a surface which can be used to attach overlays to the home root task
*/
@@ -614,6 +644,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
t.apply();
ProtoLog.v(WM_SHELL_TASK_ORG, "Removing overlay surface");
}
+ for (TaskVanishedListener l : mTaskVanishedListeners) {
+ l.onTaskVanished(taskInfo);
+ }
if (!ENABLE_SHELL_TRANSITIONS && (appearedInfo.getLeash() != null)) {
// Preemptively clean up the leash only if shell transitions are not enabled
@@ -647,6 +680,22 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
}
+ /**
+ * Shows/hides the given task surface. Not for general use as changing the task visibility may
+ * conflict with other Transitions. This is currently ONLY used to temporarily hide a task
+ * while a drag is in session.
+ */
+ public void setTaskSurfaceVisibility(int taskId, boolean visible) {
+ synchronized (mLock) {
+ final TaskAppearedInfo info = mTasks.get(taskId);
+ if (info != null) {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.setVisibility(info.getLeash(), visible);
+ t.apply();
+ }
+ }
+ }
+
private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash,
TaskListener oldListener, TaskListener newListener) {
if (oldListener == newListener) return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 87bd84017dee..7ef8d2a15fa5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -642,6 +642,7 @@ public abstract class WMShellModule {
ShellInit shellInit,
ShellController shellController,
ShellCommandHandler shellCommandHandler,
+ ShellTaskOrganizer shellTaskOrganizer,
DisplayController displayController,
UiEventLogger uiEventLogger,
IconProvider iconProvider,
@@ -649,8 +650,8 @@ public abstract class WMShellModule {
Transitions transitions,
@ShellMainThread ShellExecutor mainExecutor) {
return new DragAndDropController(context, shellInit, shellController, shellCommandHandler,
- displayController, uiEventLogger, iconProvider, globalDragListener, transitions,
- mainExecutor);
+ shellTaskOrganizer, displayController, uiEventLogger, iconProvider,
+ globalDragListener, transitions, mainExecutor);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index c374eb8e8f03..280f5f610b6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -52,6 +52,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
@@ -62,6 +63,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
@@ -85,6 +87,7 @@ import java.util.function.Function;
public class DragAndDropController implements RemoteCallable<DragAndDropController>,
GlobalDragListener.GlobalDragListenerCallback,
DisplayController.OnDisplaysChangedListener,
+ ShellTaskOrganizer.TaskVanishedListener,
View.OnDragListener, ComponentCallbacks2 {
private static final String TAG = DragAndDropController.class.getSimpleName();
@@ -92,6 +95,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
private final Context mContext;
private final ShellController mShellController;
private final ShellCommandHandler mShellCommandHandler;
+ private final ShellTaskOrganizer mShellTaskOrganizer;
private final DisplayController mDisplayController;
private final DragAndDropEventLogger mLogger;
private final IconProvider mIconProvider;
@@ -133,6 +137,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
ShellInit shellInit,
ShellController shellController,
ShellCommandHandler shellCommandHandler,
+ ShellTaskOrganizer shellTaskOrganizer,
DisplayController displayController,
UiEventLogger uiEventLogger,
IconProvider iconProvider,
@@ -142,6 +147,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
mContext = context;
mShellController = shellController;
mShellCommandHandler = shellCommandHandler;
+ mShellTaskOrganizer = shellTaskOrganizer;
mDisplayController = displayController;
mLogger = new DragAndDropEventLogger(uiEventLogger);
mIconProvider = iconProvider;
@@ -163,6 +169,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
}, 0);
mShellController.addExternalInterface(KEY_EXTRA_SHELL_DRAG_AND_DROP,
this::createExternalInterface, this);
+ mShellTaskOrganizer.addTaskVanishedListener(this);
mShellCommandHandler.addDumpCallback(this::dump, this);
mGlobalDragListener.setListener(this);
}
@@ -280,6 +287,34 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
mDisplayDropTargets.remove(displayId);
}
+ @Override
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ if (taskInfo.baseIntent == null) {
+ // Invalid info
+ return;
+ }
+ // Find the active drag
+ PerDisplay pd = null;
+ for (int i = 0; i < mDisplayDropTargets.size(); i++) {
+ final PerDisplay iPd = mDisplayDropTargets.valueAt(i);
+ if (iPd.isHandlingDrag) {
+ pd = iPd;
+ break;
+ }
+ }
+ if (pd == null || pd.activeDragCount <= 0 || !pd.isHandlingDrag) {
+ // Not currently dragging
+ return;
+ }
+
+ // Update the drag session
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Handling vanished task: id=%d component=%s", taskInfo.taskId,
+ taskInfo.baseIntent.getComponent());
+ pd.dragSession.updateRunningTask();
+ pd.dragLayout.updateSession(pd.dragSession);
+ }
+
@Override
public boolean onDrag(View target, DragEvent event) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
@@ -298,9 +333,10 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
mActiveDragDisplay = displayId;
pd.isHandlingDrag = DragUtils.canHandleDrag(event);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
- "Clip description: handlingDrag=%b itemCount=%d mimeTypes=%s",
+ "Clip description: handlingDrag=%b itemCount=%d mimeTypes=%s flags=%s",
pd.isHandlingDrag, event.getClipData().getItemCount(),
- DragUtils.getMimeTypesConcatenated(description));
+ DragUtils.getMimeTypesConcatenated(description),
+ DragUtils.dragFlagsToString(event.getDragFlags()));
}
if (!pd.isHandlingDrag) {
@@ -313,13 +349,18 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
Slog.w(TAG, "Unexpected drag start during an active drag");
return false;
}
- // TODO(b/290391688): Also update the session data with task stack changes
pd.dragSession = new DragSession(ActivityTaskManager.getInstance(),
mDisplayController.getDisplayLayout(displayId), event.getClipData(),
event.getDragFlags());
- pd.dragSession.update();
+ pd.dragSession.initialize();
pd.activeDragCount++;
pd.dragLayout.prepare(pd.dragSession, mLogger.logStart(pd.dragSession));
+ if (pd.dragSession.hideDragSourceTaskId != -1) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Hiding task surface: taskId=%d", pd.dragSession.hideDragSourceTaskId);
+ mShellTaskOrganizer.setTaskSurfaceVisibility(
+ pd.dragSession.hideDragSourceTaskId, false /* visible */);
+ }
setDropTargetWindowVisibility(pd, View.VISIBLE);
notifyListeners(l -> {
l.onDragStarted();
@@ -349,6 +390,13 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
if (pd.dragLayout.hasDropped()) {
mLogger.logDrop();
} else {
+ if (pd.dragSession.hideDragSourceTaskId != -1) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Re-showing task surface: taskId=%d",
+ pd.dragSession.hideDragSourceTaskId);
+ mShellTaskOrganizer.setTaskSurfaceVisibility(
+ pd.dragSession.hideDragSourceTaskId, true /* visible */);
+ }
pd.activeDragCount--;
pd.dragLayout.hide(event, () -> {
if (pd.activeDragCount == 0) {
@@ -402,7 +450,16 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
private boolean handleDrop(DragEvent event, PerDisplay pd) {
final SurfaceControl dragSurface = event.getDragSurface();
pd.activeDragCount--;
- return pd.dragLayout.drop(event, dragSurface, () -> {
+ // Find the token of the task to hide as a part of entering split
+ WindowContainerToken hideTaskToken = null;
+ if (pd.dragSession.hideDragSourceTaskId != -1) {
+ ActivityManager.RunningTaskInfo info = mShellTaskOrganizer.getRunningTaskInfo(
+ pd.dragSession.hideDragSourceTaskId);
+ if (info != null) {
+ hideTaskToken = info.token;
+ }
+ }
+ return pd.dragLayout.drop(event, dragSurface, hideTaskToken, () -> {
if (pd.activeDragCount == 0) {
// Hide the window if another drag hasn't been started while animating the drop
setDropTargetWindowVisibility(pd, View.INVISIBLE);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index a42ca1905ee7..5644155d74f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -59,6 +59,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
+import android.window.WindowContainerToken;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
@@ -84,7 +85,10 @@ public class DragAndDropPolicy {
private static final String TAG = DragAndDropPolicy.class.getSimpleName();
private final Context mContext;
- private final Starter mStarter;
+ // Used only for launching a fullscreen task (or as a fallback if there is no split starter)
+ private final Starter mFullscreenStarter;
+ // Used for launching tasks into splitscreen
+ private final Starter mSplitscreenStarter;
private final SplitScreenController mSplitScreen;
private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>();
private final RectF mDisallowHitRegion = new RectF();
@@ -97,10 +101,12 @@ public class DragAndDropPolicy {
}
@VisibleForTesting
- DragAndDropPolicy(Context context, SplitScreenController splitScreen, Starter starter) {
+ DragAndDropPolicy(Context context, SplitScreenController splitScreen,
+ Starter fullscreenStarter) {
mContext = context;
mSplitScreen = splitScreen;
- mStarter = mSplitScreen != null ? mSplitScreen : starter;
+ mFullscreenStarter = fullscreenStarter;
+ mSplitscreenStarter = splitScreen;
}
/**
@@ -229,8 +235,13 @@ public class DragAndDropPolicy {
return null;
}
+ /**
+ * Handles the drop on a given {@param target}. If a {@param hideTaskToken} is set, then the
+ * handling of the drop will attempt to hide the given task as a part of the same window
+ * container transaction if possible.
+ */
@VisibleForTesting
- void handleDrop(Target target) {
+ void handleDrop(Target target, @Nullable WindowContainerToken hideTaskToken) {
if (target == null || !mTargets.contains(target)) {
return;
}
@@ -245,17 +256,21 @@ public class DragAndDropPolicy {
mSplitScreen.onDroppedToSplit(position, mLoggerSessionId);
}
+ final Starter starter = target.type == TYPE_FULLSCREEN
+ ? mFullscreenStarter
+ : mSplitscreenStarter;
if (mSession.appData != null) {
- launchApp(mSession, position);
+ launchApp(mSession, starter, position, hideTaskToken);
} else {
- launchIntent(mSession, position);
+ launchIntent(mSession, starter, position, hideTaskToken);
}
}
/**
* Launches an app provided by SysUI.
*/
- private void launchApp(DragSession session, @SplitPosition int position) {
+ private void launchApp(DragSession session, Starter starter, @SplitPosition int position,
+ @Nullable WindowContainerToken hideTaskToken) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching app data at position=%d",
position);
final ClipDescription description = session.getClipDescription();
@@ -275,11 +290,15 @@ public class DragAndDropPolicy {
if (isTask) {
final int taskId = session.appData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
- mStarter.startTask(taskId, position, opts);
+ starter.startTask(taskId, position, opts, hideTaskToken);
} else if (isShortcut) {
+ if (hideTaskToken != null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Can not hide task token with starting shortcut");
+ }
final String packageName = session.appData.getStringExtra(EXTRA_PACKAGE_NAME);
final String id = session.appData.getStringExtra(EXTRA_SHORTCUT_ID);
- mStarter.startShortcut(packageName, id, position, opts, user);
+ starter.startShortcut(packageName, id, position, opts, user);
} else {
final PendingIntent launchIntent =
session.appData.getParcelableExtra(EXTRA_PENDING_INTENT);
@@ -288,15 +307,16 @@ public class DragAndDropPolicy {
Log.e(TAG, "Expected app intent's EXTRA_USER to match pending intent user");
}
}
- mStarter.startIntent(launchIntent, user.getIdentifier(), null /* fillIntent */,
- position, opts);
+ starter.startIntent(launchIntent, user.getIdentifier(), null /* fillIntent */,
+ position, opts, hideTaskToken);
}
}
/**
* Launches an intent sender provided by an application.
*/
- private void launchIntent(DragSession session, @SplitPosition int position) {
+ private void launchIntent(DragSession session, Starter starter, @SplitPosition int position,
+ @Nullable WindowContainerToken hideTaskToken) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching intent at position=%d",
position);
final ActivityOptions baseActivityOpts = ActivityOptions.makeBasic();
@@ -309,20 +329,22 @@ public class DragAndDropPolicy {
| FLAG_ACTIVITY_MULTIPLE_TASK);
final Bundle opts = baseActivityOpts.toBundle();
- mStarter.startIntent(session.launchableIntent,
+ starter.startIntent(session.launchableIntent,
session.launchableIntent.getCreatorUserHandle().getIdentifier(),
- null /* fillIntent */, position, opts);
+ null /* fillIntent */, position, opts, hideTaskToken);
}
/**
* Interface for actually committing the task launches.
*/
public interface Starter {
- void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options);
+ void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options,
+ @Nullable WindowContainerToken hideTaskToken);
void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
@Nullable Bundle options, UserHandle user);
void startIntent(PendingIntent intent, int userId, Intent fillInIntent,
- @SplitPosition int position, @Nullable Bundle options);
+ @SplitPosition int position, @Nullable Bundle options,
+ @Nullable WindowContainerToken hideTaskToken);
void enterSplitScreen(int taskId, boolean leftOrTop);
/**
@@ -344,7 +366,12 @@ public class DragAndDropPolicy {
}
@Override
- public void startTask(int taskId, int position, @Nullable Bundle options) {
+ public void startTask(int taskId, int position, @Nullable Bundle options,
+ @Nullable WindowContainerToken hideTaskToken) {
+ if (hideTaskToken != null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Default starter does not support hide task token");
+ }
try {
ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
} catch (RemoteException e) {
@@ -367,7 +394,12 @@ public class DragAndDropPolicy {
@Override
public void startIntent(PendingIntent intent, int userId, @Nullable Intent fillInIntent,
- int position, @Nullable Bundle options) {
+ int position, @Nullable Bundle options,
+ @Nullable WindowContainerToken hideTaskToken) {
+ if (hideTaskToken != null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Default starter does not support hide task token");
+ }
try {
intent.send(mContext, 0, fillInIntent, null, null, null, options);
} catch (PendingIntent.CanceledException e) {
@@ -420,7 +452,7 @@ public class DragAndDropPolicy {
@Override
public String toString() {
- return "Target {hit=" + hitRegion + " draw=" + drawRegion + "}";
+ return "Target {type=" + type + " hit=" + hitRegion + " draw=" + drawRegion + "}";
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index 4bb10dfdf8c6..e458daf9c61b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -42,6 +42,7 @@ import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
@@ -51,8 +52,10 @@ import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.widget.LinearLayout;
+import android.window.WindowContainerToken;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceId;
import com.android.internal.protolog.common.ProtoLog;
@@ -102,6 +105,8 @@ public class DragLayout extends LinearLayout
private boolean mIsShowing;
private boolean mHasDropped;
private DragSession mSession;
+ // The last position that was handled by the drag layout
+ private final Point mLastPosition = new Point();
@SuppressLint("WrongConstant")
public DragLayout(Context context, SplitScreenController splitScreenController,
@@ -265,6 +270,15 @@ public class DragLayout extends LinearLayout
*/
public void prepare(DragSession session, InstanceId loggerSessionId) {
mPolicy.start(session, loggerSessionId);
+ updateSession(session);
+ }
+
+ /**
+ * Updates the drag layout based on the diven drag session.
+ */
+ public void updateSession(DragSession session) {
+ // Note: The policy currently just keeps a reference to the session
+ boolean updatingExistingSession = mSession != null;
mSession = session;
mHasDropped = false;
mCurrentTarget = null;
@@ -280,6 +294,8 @@ public class DragLayout extends LinearLayout
int bgColor1 = getResizingBackgroundColor(taskInfo1).toArgb();
mDropZoneView1.setAppInfo(bgColor1, icon1);
mDropZoneView2.setAppInfo(bgColor1, icon1);
+ mDropZoneView1.setForceIgnoreBottomMargin(false);
+ mDropZoneView2.setForceIgnoreBottomMargin(false);
updateDropZoneSizes(null, null); // passing null splits the views evenly
} else {
// We use the first drop zone to show the fullscreen highlight, and don't need
@@ -312,6 +328,11 @@ public class DragLayout extends LinearLayout
updateDropZoneSizes(topOrLeftBounds, bottomOrRightBounds);
}
requestLayout();
+ if (updatingExistingSession) {
+ // Update targets if we are already currently dragging
+ recomputeDropTargets();
+ update(mLastPosition.x, mLastPosition.y);
+ }
}
private void updateDropZoneSizesForSingleTask() {
@@ -359,6 +380,9 @@ public class DragLayout extends LinearLayout
mDropZoneView2.setLayoutParams(dropZoneView2);
}
+ /**
+ * Shows the drag layout.
+ */
public void show() {
mIsShowing = true;
recomputeDropTargets();
@@ -384,13 +408,19 @@ public class DragLayout extends LinearLayout
* Updates the visible drop target as the user drags.
*/
public void update(DragEvent event) {
+ update((int) event.getX(), (int) event.getY());
+ }
+
+ /**
+ * Updates the visible drop target as the user drags to the given coordinates.
+ */
+ private void update(int x, int y) {
if (mHasDropped) {
return;
}
// Find containing region, if the same as mCurrentRegion, then skip, otherwise, animate the
// visibility of the current region
- DragAndDropPolicy.Target target = mPolicy.getTargetAtLocation(
- (int) event.getX(), (int) event.getY());
+ DragAndDropPolicy.Target target = mPolicy.getTargetAtLocation(x, y);
if (mCurrentTarget != target) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Current target: %s", target);
if (target == null) {
@@ -429,6 +459,7 @@ public class DragLayout extends LinearLayout
}
mCurrentTarget = target;
}
+ mLastPosition.set(x, y);
}
/**
@@ -436,6 +467,7 @@ public class DragLayout extends LinearLayout
*/
public void hide(DragEvent event, Runnable hideCompleteCallback) {
mIsShowing = false;
+ mLastPosition.set(-1, -1);
animateSplitContainers(false, () -> {
if (hideCompleteCallback != null) {
hideCompleteCallback.run();
@@ -456,13 +488,13 @@ public class DragLayout extends LinearLayout
/**
* Handles the drop onto a target and animates out the visible drop targets.
*/
- public boolean drop(DragEvent event, SurfaceControl dragSurface,
- Runnable dropCompleteCallback) {
+ public boolean drop(DragEvent event, @NonNull SurfaceControl dragSurface,
+ @Nullable WindowContainerToken hideTaskToken, Runnable dropCompleteCallback) {
final boolean handledDrop = mCurrentTarget != null;
mHasDropped = true;
// Process the drop
- mPolicy.handleDrop(mCurrentTarget);
+ mPolicy.handleDrop(mCurrentTarget, hideTaskToken);
// Start animating the drop UI out with the drag surface
hide(event, dropCompleteCallback);
@@ -472,7 +504,7 @@ public class DragLayout extends LinearLayout
return handledDrop;
}
- private void hideDragSurface(SurfaceControl dragSurface) {
+ private void hideDragSurface(@NonNull SurfaceControl dragSurface) {
final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
final ValueAnimator dragSurfaceAnimator = ValueAnimator.ofFloat(0f, 1f);
// Currently the splash icon animation runs with the default ValueAnimator duration of
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
index 0addd432aff0..9a652f21c0be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.draganddrop;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.ClipDescription.EXTRA_HIDE_DRAG_SOURCE_TASK_ID;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -27,10 +28,13 @@ import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.os.PersistableBundle;
import androidx.annotation.Nullable;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.List;
@@ -61,6 +65,7 @@ public class DragSession {
@WindowConfiguration.ActivityType
int runningTaskActType = ACTIVITY_TYPE_STANDARD;
boolean dragItemSupportsSplitscreen;
+ int hideDragSourceTaskId = -1;
DragSession(ActivityTaskManager activityTaskManager,
DisplayLayout dispLayout, ClipData data, int dragFlags) {
@@ -68,6 +73,11 @@ public class DragSession {
mInitialDragData = data;
mInitialDragFlags = dragFlags;
displayLayout = dispLayout;
+ hideDragSourceTaskId = data.getDescription().getExtras() != null
+ ? data.getDescription().getExtras().getInt(EXTRA_HIDE_DRAG_SOURCE_TASK_ID, -1)
+ : -1;
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Extracting drag source taskId: taskId=%d", hideDragSourceTaskId);
}
/**
@@ -79,17 +89,38 @@ public class DragSession {
}
/**
- * Updates the session data based on the current state of the system.
+ * Updates the running task for this drag session.
*/
- void update() {
- List<ActivityManager.RunningTaskInfo> tasks =
- mActivityTaskManager.getTasks(1, false /* filterOnlyVisibleRecents */);
+ void updateRunningTask() {
+ final boolean hideDragSourceTask = hideDragSourceTaskId != -1;
+ final List<ActivityManager.RunningTaskInfo> tasks =
+ mActivityTaskManager.getTasks(hideDragSourceTask ? 2 : 1,
+ false /* filterOnlyVisibleRecents */);
if (!tasks.isEmpty()) {
- final ActivityManager.RunningTaskInfo task = tasks.get(0);
- runningTaskInfo = task;
- runningTaskWinMode = task.getWindowingMode();
- runningTaskActType = task.getActivityType();
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ final ActivityManager.RunningTaskInfo task = tasks.get(i);
+ if (hideDragSourceTask && hideDragSourceTaskId == task.taskId) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Skipping running task: id=%d component=%s", task.taskId,
+ task.baseIntent != null ? task.baseIntent.getComponent() : "null");
+ continue;
+ }
+ runningTaskInfo = task;
+ runningTaskWinMode = task.getWindowingMode();
+ runningTaskActType = task.getActivityType();
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Running task: id=%d component=%s", task.taskId,
+ task.baseIntent != null ? task.baseIntent.getComponent() : "null");
+ break;
+ }
}
+ }
+
+ /**
+ * Updates the session data based on the current state of the system at the start of the drag.
+ */
+ void initialize() {
+ updateRunningTask();
activityInfo = mInitialDragData.getItemAt(0).getActivityInfo();
// TODO: This should technically check & respect config_supportsNonResizableMultiWindow
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
index e215870f1894..22cfa328bfda 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
@@ -19,16 +19,28 @@ package com.android.wm.shell.draganddrop;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+import static android.view.View.DRAG_FLAG_ACCESSIBILITY_ACTION;
+import static android.view.View.DRAG_FLAG_GLOBAL;
+import static android.view.View.DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION;
+import static android.view.View.DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION;
+import static android.view.View.DRAG_FLAG_GLOBAL_SAME_APPLICATION;
+import static android.view.View.DRAG_FLAG_GLOBAL_URI_READ;
+import static android.view.View.DRAG_FLAG_GLOBAL_URI_WRITE;
+import static android.view.View.DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START;
+import static android.view.View.DRAG_FLAG_OPAQUE;
+import static android.view.View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION;
+import static android.view.View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG;
import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipDescription;
import android.view.DragEvent;
-import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.StringJoiner;
+
/** Collection of utility classes for handling drag and drop. */
public class DragUtils {
private static final String TAG = "DragUtils";
@@ -76,7 +88,7 @@ public class DragUtils {
*/
@Nullable
public static PendingIntent getLaunchIntent(@NonNull ClipData data, int dragFlags) {
- if ((dragFlags & View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) == 0) {
+ if ((dragFlags & DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) == 0) {
// Disallow launching the intent if the app does not want to delegate it to the system
return null;
}
@@ -105,4 +117,35 @@ public class DragUtils {
}
return mimeTypes;
}
+
+ /**
+ * Returns the string description of the given {@param dragFlags}.
+ */
+ public static String dragFlagsToString(int dragFlags) {
+ StringJoiner str = new StringJoiner("|");
+ if ((dragFlags & DRAG_FLAG_GLOBAL) != 0) {
+ str.add("GLOBAL");
+ } else if ((dragFlags & DRAG_FLAG_GLOBAL_URI_READ) != 0) {
+ str.add("GLOBAL_URI_READ");
+ } else if ((dragFlags & DRAG_FLAG_GLOBAL_URI_WRITE) != 0) {
+ str.add("GLOBAL_URI_WRITE");
+ } else if ((dragFlags & DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION) != 0) {
+ str.add("GLOBAL_PERSISTABLE_URI_PERMISSION");
+ } else if ((dragFlags & DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION) != 0) {
+ str.add("GLOBAL_PREFIX_URI_PERMISSION");
+ } else if ((dragFlags & DRAG_FLAG_OPAQUE) != 0) {
+ str.add("OPAQUE");
+ } else if ((dragFlags & DRAG_FLAG_ACCESSIBILITY_ACTION) != 0) {
+ str.add("ACCESSIBILITY_ACTION");
+ } else if ((dragFlags & DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION) != 0) {
+ str.add("REQUEST_SURFACE_FOR_RETURN_ANIMATION");
+ } else if ((dragFlags & DRAG_FLAG_GLOBAL_SAME_APPLICATION) != 0) {
+ str.add("GLOBAL_SAME_APPLICATION");
+ } else if ((dragFlags & DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) != 0) {
+ str.add("START_INTENT_SENDER_ON_UNHANDLED_DRAG");
+ } else if ((dragFlags & DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START) != 0) {
+ str.add("HIDE_CALLING_TASK_ON_DRAG_START");
+ }
+ return str.toString();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
index 724a130ef52d..a72bae29a9c2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.draganddrop;
import static com.android.wm.shell.animation.Interpolators.FAST_OUT_SLOW_IN;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
@@ -37,13 +38,16 @@ import android.widget.ImageView;
import androidx.annotation.Nullable;
import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
/**
* Renders a drop zone area for items being dragged.
*/
public class DropZoneView extends FrameLayout {
+ private static final boolean DEBUG_LAYOUT = false;
private static final float SPLASHSCREEN_ALPHA = 0.90f;
private static final float HIGHLIGHT_ALPHA = 1f;
private static final int MARGIN_ANIMATION_ENTER_DURATION = 400;
@@ -77,6 +81,7 @@ public class DropZoneView extends FrameLayout {
private int mHighlightColor;
private ObjectAnimator mBackgroundAnimator;
+ private int mTargetBackgroundColor;
private ObjectAnimator mMarginAnimator;
private float mMarginPercent;
@@ -146,6 +151,10 @@ public class DropZoneView extends FrameLayout {
/** Ignores the bottom margin provided by the insets. */
public void setForceIgnoreBottomMargin(boolean ignoreBottomMargin) {
+ if (DEBUG_LAYOUT) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "setForceIgnoreBottomMargin: ignore=%b", ignoreBottomMargin);
+ }
mIgnoreBottomMargin = ignoreBottomMargin;
if (mMarginPercent > 0) {
mMarginView.invalidate();
@@ -154,8 +163,14 @@ public class DropZoneView extends FrameLayout {
/** Sets the bottom inset so the drop zones are above bottom navigation. */
public void setBottomInset(float bottom) {
+ if (DEBUG_LAYOUT) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "setBottomInset: inset=%f",
+ bottom);
+ }
mBottomInset = bottom;
- ((LayoutParams) mSplashScreenView.getLayoutParams()).bottomMargin = (int) bottom;
+ final LayoutParams lp = (LayoutParams) mSplashScreenView.getLayoutParams();
+ lp.bottomMargin = (int) bottom;
+ mSplashScreenView.setLayoutParams(lp);
if (mMarginPercent > 0) {
mMarginView.invalidate();
}
@@ -181,6 +196,9 @@ public class DropZoneView extends FrameLayout {
/** Animates between highlight and splashscreen depending on current state. */
public void animateSwitch() {
+ if (DEBUG_LAYOUT) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "animateSwitch");
+ }
mShowingHighlight = !mShowingHighlight;
mShowingSplash = !mShowingHighlight;
final int newColor = mShowingHighlight ? mHighlightColor : mSplashScreenColor;
@@ -190,6 +208,10 @@ public class DropZoneView extends FrameLayout {
/** Animates the highlight indicating the zone is hovered on or not. */
public void setShowingHighlight(boolean showingHighlight) {
+ if (DEBUG_LAYOUT) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "setShowingHighlight: showing=%b",
+ showingHighlight);
+ }
mShowingHighlight = showingHighlight;
mShowingSplash = !mShowingHighlight;
final int newColor = mShowingHighlight ? mHighlightColor : mSplashScreenColor;
@@ -199,6 +221,10 @@ public class DropZoneView extends FrameLayout {
/** Animates the margins around the drop zone to show or hide. */
public void setShowingMargin(boolean visible) {
+ if (DEBUG_LAYOUT) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "setShowingMargin: visible=%b",
+ visible);
+ }
if (mShowingMargin != visible) {
mShowingMargin = visible;
animateMarginToState();
@@ -212,6 +238,15 @@ public class DropZoneView extends FrameLayout {
}
private void animateBackground(int startColor, int endColor) {
+ if (DEBUG_LAYOUT) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "animateBackground: start=%s end=%s",
+ Integer.toHexString(startColor), Integer.toHexString(endColor));
+ }
+ if (endColor == mTargetBackgroundColor) {
+ // Already at, or animating to, that background color
+ return;
+ }
if (mBackgroundAnimator != null) {
mBackgroundAnimator.cancel();
}
@@ -223,6 +258,7 @@ public class DropZoneView extends FrameLayout {
mBackgroundAnimator.setInterpolator(FAST_OUT_SLOW_IN);
}
mBackgroundAnimator.start();
+ mTargetBackgroundColor = endColor;
}
private void animateSplashScreenIcon() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 03c8cf8cc795..417b37b1f16f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -37,6 +37,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.IRecentsAnimationRunner;
+import android.window.WindowContainerToken;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
@@ -453,11 +454,31 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
/**
- * Find the background task that match the given component.
+ * Returns the top running leaf task ignoring {@param ignoreTaskToken} if it is specified.
+ * NOTE: This path currently makes assumptions that ignoreTaskToken is for the top task.
+ */
+ @Nullable
+ public ActivityManager.RunningTaskInfo getTopRunningTask(
+ @Nullable WindowContainerToken ignoreTaskToken) {
+ List<ActivityManager.RunningTaskInfo> tasks = mActivityTaskManager.getTasks(2,
+ false /* filterOnlyVisibleRecents */);
+ for (int i = 0; i < tasks.size(); i++) {
+ final ActivityManager.RunningTaskInfo task = tasks.get(i);
+ if (task.token.equals(ignoreTaskToken)) {
+ continue;
+ }
+ return task;
+ }
+ return null;
+ }
+
+ /**
+ * Find the background task that match the given component. Ignores tasks match
+ * {@param ignoreTaskToken} if it is non-null.
*/
@Nullable
public ActivityManager.RecentTaskInfo findTaskInBackground(ComponentName componentName,
- int userId) {
+ int userId, @Nullable WindowContainerToken ignoreTaskToken) {
if (componentName == null) {
return null;
}
@@ -469,6 +490,9 @@ public class RecentTasksController implements TaskStackListenerCallback,
if (task.isVisible) {
continue;
}
+ if (task.token.equals(ignoreTaskToken)) {
+ continue;
+ }
if (componentName.equals(task.baseIntent.getComponent()) && userId == task.userId) {
return task;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index dd219d32bbaa..36cc9101b0dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -64,6 +64,7 @@ import android.view.SurfaceSession;
import android.view.WindowManager;
import android.widget.Toast;
import android.window.RemoteTransition;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
@@ -526,7 +527,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator.requestEnterSplitSelect(taskInfo, wct, splitPosition, taskBounds);
}
- public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+ /**
+ * Starts an existing task into split.
+ * TODO(b/351900580): We should remove this path and use StageCoordinator#startTask() instead
+ * @param hideTaskToken is not supported.
+ */
+ public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options,
+ @Nullable WindowContainerToken hideTaskToken) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Legacy startTask does not support hide task token");
final int[] result = new int[1];
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
@Override
@@ -584,8 +593,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
if (options == null) options = new Bundle();
final ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
- if (samePackage(packageName, getPackageName(reverseSplitPosition(position)),
- user.getIdentifier(), getUserId(reverseSplitPosition(position)))) {
+ if (samePackage(packageName, getPackageName(reverseSplitPosition(position), null),
+ user.getIdentifier(), getUserId(reverseSplitPosition(position), null))) {
if (mMultiInstanceHelpher.supportsMultiInstanceSplit(
getShortcutComponent(packageName, shortcutId, user, mLauncherApps))) {
activityOptions.setApplyMultipleTaskFlagForShortcut(true);
@@ -676,10 +685,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
* See {@link #startIntent(PendingIntent, int, Intent, int, Bundle)}
* @param instanceId to be used by {@link SplitscreenEventLogger}
*/
- public void startIntent(PendingIntent intent, int userId, @Nullable Intent fillInIntent,
- @SplitPosition int position, @Nullable Bundle options, @NonNull InstanceId instanceId) {
+ public void startIntentWithInstanceId(PendingIntent intent, int userId,
+ @Nullable Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options,
+ @NonNull InstanceId instanceId) {
mStageCoordinator.onRequestToSplit(instanceId, ENTER_REASON_LAUNCHER);
- startIntent(intent, userId, fillInIntent, position, options);
+ startIntent(intent, userId, fillInIntent, position, options, null /* hideTaskToken */);
}
private void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
@@ -825,9 +835,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
instanceId);
}
+ /**
+ * Starts the given intent into split.
+ * @param hideTaskToken If non-null, a task matching this token will be moved to back in the
+ * same window container transaction as the starting of the intent.
+ */
@Override
public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent,
- @SplitPosition int position, @Nullable Bundle options) {
+ @SplitPosition int position, @Nullable Bundle options,
+ @Nullable WindowContainerToken hideTaskToken) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"startIntent(): intent=%s user=%d fillInIntent=%s position=%d", intent, userId1,
fillInIntent, position);
@@ -838,23 +854,24 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
final String packageName1 = SplitScreenUtils.getPackageName(intent);
- final String packageName2 = getPackageName(reverseSplitPosition(position));
- final int userId2 = getUserId(reverseSplitPosition(position));
+ final String packageName2 = getPackageName(reverseSplitPosition(position), hideTaskToken);
+ final int userId2 = getUserId(reverseSplitPosition(position), hideTaskToken);
final ComponentName component = intent.getIntent().getComponent();
// To prevent accumulating large number of instances in the background, reuse task
// in the background. If we don't explicitly reuse, new may be created even if the app
// isn't multi-instance because WM won't automatically remove/reuse the previous instance
final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
- .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1))
+ .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1,
+ hideTaskToken))
.orElse(null);
if (taskInfo != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"Found suitable background task=%s", taskInfo);
if (ENABLE_SHELL_TRANSITIONS) {
- mStageCoordinator.startTask(taskInfo.taskId, position, options);
+ mStageCoordinator.startTask(taskInfo.taskId, position, options, hideTaskToken);
} else {
- startTask(taskInfo.taskId, position, options);
+ startTask(taskInfo.taskId, position, options, hideTaskToken);
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Start task in background");
return;
@@ -879,19 +896,23 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
}
- mStageCoordinator.startIntent(intent, fillInIntent, position, options);
+ mStageCoordinator.startIntent(intent, fillInIntent, position, options, hideTaskToken);
}
- /** Retrieve package name of a specific split position if split screen is activated, otherwise
- * returns the package name of the top running task. */
+ /**
+ * Retrieve package name of a specific split position if split screen is activated, otherwise
+ * returns the package name of the top running task.
+ * TODO(b/351900580): Merge this with getUserId() so we don't make multiple binder calls
+ */
@Nullable
- private String getPackageName(@SplitPosition int position) {
+ private String getPackageName(@SplitPosition int position,
+ @Nullable WindowContainerToken ignoreTaskToken) {
ActivityManager.RunningTaskInfo taskInfo;
if (isSplitScreenVisible()) {
taskInfo = getTaskInfo(position);
} else {
taskInfo = mRecentTasksOptional
- .map(recentTasks -> recentTasks.getTopRunningTask())
+ .map(recentTasks -> recentTasks.getTopRunningTask(ignoreTaskToken))
.orElse(null);
if (!isValidToSplit(taskInfo)) {
return null;
@@ -901,15 +922,19 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return taskInfo != null ? SplitScreenUtils.getPackageName(taskInfo.baseIntent) : null;
}
- /** Retrieve user id of a specific split position if split screen is activated, otherwise
- * returns the user id of the top running task. */
- private int getUserId(@SplitPosition int position) {
+ /**
+ * Retrieve user id of a specific split position if split screen is activated, otherwise
+ * returns the user id of the top running task.
+ * TODO: Merge this with getPackageName() so we don't make multiple binder calls
+ */
+ private int getUserId(@SplitPosition int position,
+ @Nullable WindowContainerToken ignoreTaskToken) {
ActivityManager.RunningTaskInfo taskInfo;
if (isSplitScreenVisible()) {
taskInfo = getTaskInfo(position);
} else {
taskInfo = mRecentTasksOptional
- .map(recentTasks -> recentTasks.getTopRunningTask())
+ .map(recentTasks -> recentTasks.getTopRunningTask(ignoreTaskToken))
.orElse(null);
if (!isValidToSplit(taskInfo)) {
return -1;
@@ -1290,7 +1315,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@Override
public void startTask(int taskId, int position, @Nullable Bundle options) {
executeRemoteCallWithTaskPermission(mController, "startTask",
- (controller) -> controller.startTask(taskId, position, options));
+ (controller) -> controller.startTask(taskId, position, options,
+ null /* hideTaskToken */));
}
@Override
@@ -1402,8 +1428,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
public void startIntent(PendingIntent intent, int userId, Intent fillInIntent, int position,
@Nullable Bundle options, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startIntent",
- (controller) -> controller.startIntent(intent, userId, fillInIntent, position,
- options, instanceId));
+ (controller) -> controller.startIntentWithInstanceId(intent, userId,
+ fillInIntent, position, options, instanceId));
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 4287daa03223..839700e5b8a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -592,12 +592,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- /** Use this method to launch an existing Task via a taskId */
- void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+ /**
+ * Use this method to launch an existing Task via a taskId.
+ * @param hideTaskToken If non-null, a task matching this token will be moved to back in the
+ * same window container transaction as the starting of the intent.
+ */
+ void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options,
+ @Nullable WindowContainerToken hideTaskToken) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startTask: task=%d position=%d", taskId, position);
mSplitRequest = new SplitRequest(taskId, position);
final WindowContainerTransaction wct = new WindowContainerTransaction();
options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
+ if (hideTaskToken != null) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Reordering hide-task to bottom");
+ wct.reorder(hideTaskToken, false /* onTop */);
+ }
wct.startTask(taskId, options);
// If this should be mixed, send the task to avoid split handle transition directly.
if (mMixedHandler != null && mMixedHandler.isTaskInPip(taskId, mTaskOrganizer)) {
@@ -623,9 +632,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
extraTransitType, !mIsDropEntering);
}
- /** Launches an activity into split. */
+ /**
+ * Launches an activity into split.
+ * @param hideTaskToken If non-null, a task matching this token will be moved to back in the
+ * same window container transaction as the starting of the intent.
+ */
void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
- @Nullable Bundle options) {
+ @Nullable Bundle options, @Nullable WindowContainerToken hideTaskToken) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startIntent: intent=%s position=%d", intent.getIntent(),
position);
mSplitRequest = new SplitRequest(intent.getIntent(), position);
@@ -636,6 +649,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final WindowContainerTransaction wct = new WindowContainerTransaction();
options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
+ if (hideTaskToken != null) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Reordering hide-task to bottom");
+ wct.reorder(hideTaskToken, false /* onTop */);
+ }
wct.sendPendingIntent(intent, fillInIntent, options);
// If this should be mixed, just send the intent to avoid split handle transition directly.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index f9b4108bc8c2..8303317d39fc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -687,6 +687,25 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
verify(mRecentTasksController).onTaskRunningInfoChanged(task2);
}
+ @Test
+ public void testTaskVanishedCallback() {
+ RunningTaskInfo task1 = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FULLSCREEN);
+ mOrganizer.onTaskAppeared(task1, /* leash= */ null);
+
+ RunningTaskInfo[] vanishedTasks = new RunningTaskInfo[1];
+ ShellTaskOrganizer.TaskVanishedListener listener =
+ new ShellTaskOrganizer.TaskVanishedListener() {
+ @Override
+ public void onTaskVanished(RunningTaskInfo taskInfo) {
+ vanishedTasks[0] = taskInfo;
+ }
+ };
+ mOrganizer.addTaskVanishedListener(listener);
+ mOrganizer.onTaskVanished(task1);
+
+ assertEquals(vanishedTasks[0], task1);
+ }
+
private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
RunningTaskInfo taskInfo = new RunningTaskInfo();
taskInfo.taskId = taskId;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
index a64ebd301c00..840126421c08 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
@@ -76,6 +76,8 @@ public class DragAndDropControllerTest extends ShellTestCase {
@Mock
private ShellCommandHandler mShellCommandHandler;
@Mock
+ private ShellTaskOrganizer mShellTaskOrganizer;
+ @Mock
private DisplayController mDisplayController;
@Mock
private UiEventLogger mUiEventLogger;
@@ -96,8 +98,8 @@ public class DragAndDropControllerTest extends ShellTestCase {
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mController = new DragAndDropController(mContext, mShellInit, mShellController,
- mShellCommandHandler, mDisplayController, mUiEventLogger, mIconProvider,
- mGlobalDragListener, mTransitions, mMainExecutor);
+ mShellCommandHandler, mShellTaskOrganizer, mDisplayController, mUiEventLogger,
+ mIconProvider, mGlobalDragListener, mTransitions, mMainExecutor);
mController.onInit();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 6e72e8df8d62..97fa8d6ceca9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -65,8 +65,6 @@ import android.content.res.Resources;
import android.graphics.Insets;
import android.os.RemoteException;
import android.view.DisplayInfo;
-import android.view.DragEvent;
-import android.view.View;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -76,7 +74,6 @@ import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target;
import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.startingsurface.TaskSnapshotWindow;
import org.junit.After;
import org.junit.Before;
@@ -106,6 +103,8 @@ public class DragAndDropPolicyTest extends ShellTestCase {
// Both the split-screen and start interface.
@Mock
private SplitScreenController mSplitScreenStarter;
+ @Mock
+ private DragAndDropPolicy.Starter mFullscreenStarter;
@Mock
private InstanceId mLoggerSessionId;
@@ -151,7 +150,7 @@ public class DragAndDropPolicyTest extends ShellTestCase {
mPortraitDisplayLayout = new DisplayLayout(info2, res, false, false);
mInsets = Insets.of(0, 0, 0, 0);
- mPolicy = spy(new DragAndDropPolicy(mContext, mSplitScreenStarter, mSplitScreenStarter));
+ mPolicy = spy(new DragAndDropPolicy(mContext, mSplitScreenStarter, mFullscreenStarter));
mActivityClipData = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY);
mLaunchableIntentPendingIntent = mock(PendingIntent.class);
when(mLaunchableIntentPendingIntent.getCreatorUserHandle())
@@ -285,14 +284,14 @@ public class DragAndDropPolicyTest extends ShellTestCase {
setRunningTask(mHomeTask);
DragSession dragSession = new DragSession(mActivityTaskManager,
mLandscapeDisplayLayout, data, 0 /* dragFlags */);
- dragSession.update();
+ dragSession.initialize();
mPolicy.start(dragSession, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN));
- verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
- eq(SPLIT_POSITION_UNDEFINED), any());
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), null /* hideTaskToken */);
+ verify(mFullscreenStarter).startIntent(any(), anyInt(), any(),
+ eq(SPLIT_POSITION_UNDEFINED), any(), any());
}
private void dragOverFullscreenApp_expectSplitScreenTargets(ClipData data) {
@@ -300,19 +299,19 @@ public class DragAndDropPolicyTest extends ShellTestCase {
setRunningTask(mFullscreenAppTask);
DragSession dragSession = new DragSession(mActivityTaskManager,
mLandscapeDisplayLayout, data, 0 /* dragFlags */);
- dragSession.update();
+ dragSession.initialize();
mPolicy.start(dragSession, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT));
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT), null /* hideTaskToken */);
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
- eq(SPLIT_POSITION_TOP_OR_LEFT), any());
+ eq(SPLIT_POSITION_TOP_OR_LEFT), any(), any());
reset(mSplitScreenStarter);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT));
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), null /* hideTaskToken */);
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
- eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
+ eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any(), any());
}
private void dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(ClipData data) {
@@ -320,19 +319,20 @@ public class DragAndDropPolicyTest extends ShellTestCase {
setRunningTask(mFullscreenAppTask);
DragSession dragSession = new DragSession(mActivityTaskManager,
mPortraitDisplayLayout, data, 0 /* dragFlags */);
- dragSession.update();
+ dragSession.initialize();
mPolicy.start(dragSession, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP));
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP), null /* hideTaskToken */);
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
- eq(SPLIT_POSITION_TOP_OR_LEFT), any());
+ eq(SPLIT_POSITION_TOP_OR_LEFT), any(), any());
reset(mSplitScreenStarter);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM));
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM),
+ null /* hideTaskToken */);
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
- eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
+ eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any(), any());
}
@Test
@@ -340,7 +340,7 @@ public class DragAndDropPolicyTest extends ShellTestCase {
setRunningTask(mFullscreenAppTask);
DragSession dragSession = new DragSession(mActivityTaskManager,
mLandscapeDisplayLayout, mActivityClipData, 0 /* dragFlags */);
- dragSession.update();
+ dragSession.initialize();
mPolicy.start(dragSession, mLoggerSessionId);
ArrayList<Target> targets = mPolicy.getTargets(mInsets);
for (Target t : targets) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 3c387f0d7c34..5b95b1588814 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -36,6 +36,7 @@ import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -49,6 +50,9 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
+import android.os.IBinder;
+import android.window.IWindowContainerToken;
+import android.window.WindowContainerToken;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -195,10 +199,10 @@ public class SplitScreenControllerTests extends ShellTestCase {
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
- SPLIT_POSITION_TOP_OR_LEFT, null);
+ SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */);
verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(),
- eq(SPLIT_POSITION_TOP_OR_LEFT), isNull());
+ eq(SPLIT_POSITION_TOP_OR_LEFT), isNull(), isNull());
assertEquals(FLAG_ACTIVITY_NO_USER_ACTION,
mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_NO_USER_ACTION);
}
@@ -213,19 +217,20 @@ public class SplitScreenControllerTests extends ShellTestCase {
ActivityManager.RunningTaskInfo topRunningTask =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
+ doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(any());
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
- SPLIT_POSITION_TOP_OR_LEFT, null);
+ SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */);
verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(),
- eq(SPLIT_POSITION_TOP_OR_LEFT), isNull());
+ eq(SPLIT_POSITION_TOP_OR_LEFT), isNull(), isNull());
assertEquals(FLAG_ACTIVITY_MULTIPLE_TASK,
mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK);
}
@Test
public void startIntent_multiInstancesNotSupported_startTaskInBackgroundBeforeSplitActivated() {
- doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any());
+ doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any(), any());
Intent startIntent = createStartIntent("startActivity");
PendingIntent pendingIntent =
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
@@ -233,15 +238,16 @@ public class SplitScreenControllerTests extends ShellTestCase {
ActivityManager.RunningTaskInfo topRunningTask =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
+ doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(any());
// Put the same component into a task in the background
ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo();
- doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any(), anyInt());
+ doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any(), anyInt(), any());
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
- SPLIT_POSITION_TOP_OR_LEFT, null);
+ SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */);
verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
- isNull());
+ isNull(), isNull());
verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
verify(mStageCoordinator, never()).switchSplitPosition(any());
}
@@ -249,7 +255,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
@Test
public void startIntent_multiInstancesSupported_startTaskInBackgroundAfterSplitActivated() {
doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
- doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any());
+ doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any(), any());
Intent startIntent = createStartIntent("startActivity");
PendingIntent pendingIntent =
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
@@ -261,13 +267,13 @@ public class SplitScreenControllerTests extends ShellTestCase {
SPLIT_POSITION_BOTTOM_OR_RIGHT);
// Put the same component into a task in the background
doReturn(new ActivityManager.RecentTaskInfo()).when(mRecentTasks)
- .findTaskInBackground(any(), anyInt());
+ .findTaskInBackground(any(), anyInt(), any());
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
- SPLIT_POSITION_TOP_OR_LEFT, null);
+ SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */);
verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
- isNull());
+ isNull(), isNull());
}
@Test
@@ -284,7 +290,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
SPLIT_POSITION_BOTTOM_OR_RIGHT);
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
- SPLIT_POSITION_TOP_OR_LEFT, null);
+ SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */);
verify(mStageCoordinator).switchSplitPosition(anyString());
}
@@ -312,6 +318,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
info.supportsMultiWindow = true;
info.baseIntent = strIntent;
info.baseActivity = strIntent.getComponent();
+ info.token = new WindowContainerToken(mock(IWindowContainerToken.class));
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.packageName = info.baseActivity.getPackageName();
activityInfo.name = info.baseActivity.getClassName();
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 3409c29d3c2c..defbc1142adb 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -16,8 +16,6 @@
package com.android.externalstorage;
-import static java.util.regex.Pattern.CASE_INSENSITIVE;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.StorageStatsManager;
@@ -61,12 +59,15 @@ import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.UUID;
-import java.util.regex.Pattern;
+import java.util.stream.Collectors;
/**
* Presents content of the shared (a.k.a. "external") storage.
@@ -89,12 +90,9 @@ public class ExternalStorageProvider extends FileSystemProvider {
private static final Uri BASE_URI =
new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build();
- /**
- * Regex for detecting {@code /Android/data/}, {@code /Android/obb/} and
- * {@code /Android/sandbox/} along with all their subdirectories and content.
- */
- private static final Pattern PATTERN_RESTRICTED_ANDROID_SUBTREES =
- Pattern.compile("^Android/(?:data|obb|sandbox)(?:/.+)?", CASE_INSENSITIVE);
+ private static final String PRIMARY_EMULATED_STORAGE_PATH = "/storage/emulated/";
+
+ private static final String STORAGE_PATH = "/storage/";
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
@@ -309,10 +307,69 @@ public class ExternalStorageProvider extends FileSystemProvider {
return false;
}
- final String path = getPathFromDocId(documentId);
- return PATTERN_RESTRICTED_ANDROID_SUBTREES.matcher(path).matches();
+ try {
+ final RootInfo root = getRootFromDocId(documentId);
+ final String canonicalPath = getPathFromDocId(documentId);
+ return isRestrictedPath(root.rootId, canonicalPath);
+ } catch (Exception e) {
+ return true;
+ }
}
+ /**
+ * Based on the given root id and path, we restrict path access if file is Android/data or
+ * Android/obb or Android/sandbox or one of their subdirectories.
+ *
+ * @param canonicalPath of the file
+ * @return true if path is restricted
+ */
+ private boolean isRestrictedPath(String rootId, String canonicalPath) {
+ if (rootId == null || canonicalPath == null) {
+ return true;
+ }
+
+ final String rootPath;
+ if (rootId.equalsIgnoreCase(ROOT_ID_PRIMARY_EMULATED)) {
+ // Creates "/storage/emulated/<user-id>"
+ rootPath = PRIMARY_EMULATED_STORAGE_PATH + UserHandle.myUserId();
+ } else {
+ // Creates "/storage/<volume-uuid>"
+ rootPath = STORAGE_PATH + rootId;
+ }
+ List<java.nio.file.Path> restrictedPathList = Arrays.asList(
+ Paths.get(rootPath, "Android", "data"),
+ Paths.get(rootPath, "Android", "obb"),
+ Paths.get(rootPath, "Android", "sandbox"));
+ // We need to identify restricted parent paths which actually exist on the device
+ List<java.nio.file.Path> validRestrictedPathsToCheck = restrictedPathList.stream().filter(
+ Files::exists).collect(Collectors.toList());
+
+ boolean isRestricted = false;
+ java.nio.file.Path filePathToCheck = Paths.get(rootPath, canonicalPath);
+ try {
+ while (filePathToCheck != null) {
+ for (java.nio.file.Path restrictedPath : validRestrictedPathsToCheck) {
+ if (Files.isSameFile(restrictedPath, filePathToCheck)) {
+ isRestricted = true;
+ Log.v(TAG, "Restricting access for path: " + filePathToCheck);
+ break;
+ }
+ }
+ if (isRestricted) {
+ break;
+ }
+
+ filePathToCheck = filePathToCheck.getParent();
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Error in checking file equality check.", e);
+ isRestricted = true;
+ }
+
+ return isRestricted;
+ }
+
+
/**
* Check that the directory is the root of storage or blocked file from tree.
* <p>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index d8486029a903..073f33fe5245 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -33,7 +33,6 @@ import com.android.systemui.res.R;
import com.android.systemui.shade.TouchLogger;
import com.android.systemui.statusbar.CrossFadeHelper;
-import java.io.PrintWriter;
import java.util.Set;
/**
@@ -117,18 +116,6 @@ public class KeyguardStatusView extends GridLayout {
return TouchLogger.logDispatchTouch(TAG, ev, super.dispatchTouchEvent(ev));
}
- public void dump(PrintWriter pw, String[] args) {
- pw.println("KeyguardStatusView:");
- pw.println(" mDarkAmount: " + mDarkAmount);
- pw.println(" visibility: " + getVisibility());
- if (mClockView != null) {
- mClockView.dump(pw, args);
- }
- if (mKeyguardSlice != null) {
- mKeyguardSlice.dump(pw, args);
- }
- }
-
@Override
public ViewPropertyAnimator animate() {
if (Build.IS_DEBUGGABLE) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 603a47e8d26e..63a4af949c8c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -48,9 +48,7 @@ import com.android.app.animation.Interpolators;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.keyguard.KeyguardClockSwitch.ClockSize;
import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.Dumpable;
import com.android.systemui.animation.ViewHierarchyAnimator;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.plugins.clocks.ClockController;
@@ -70,15 +68,12 @@ import com.android.systemui.util.ViewController;
import kotlin.coroutines.CoroutineContext;
import kotlin.coroutines.EmptyCoroutineContext;
-import java.io.PrintWriter;
-
import javax.inject.Inject;
/**
* Injectable controller for {@link KeyguardStatusView}.
*/
-public class KeyguardStatusViewController extends ViewController<KeyguardStatusView> implements
- Dumpable {
+public class KeyguardStatusViewController extends ViewController<KeyguardStatusView> {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
@VisibleForTesting static final String TAG = "KeyguardStatusViewController";
private static final long STATUS_AREA_HEIGHT_ANIMATION_MILLIS = 133;
@@ -108,7 +103,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
private Boolean mSplitShadeEnabled = false;
private Boolean mStatusViewCentered = true;
- private DumpManager mDumpManager;
private final TransitionListenerAdapter mKeyguardStatusAlignmentTransitionListener =
new TransitionListenerAdapter() {
@@ -176,7 +170,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
KeyguardLogger logger,
InteractionJankMonitor interactionJankMonitor,
KeyguardInteractor keyguardInteractor,
- DumpManager dumpManager,
PowerInteractor powerInteractor) {
super(keyguardStatusView);
mKeyguardSliceViewController = keyguardSliceViewController;
@@ -188,7 +181,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
dozeParameters, screenOffAnimationController, /* animateYPos= */ true,
logger.getBuffer());
mInteractionJankMonitor = interactionJankMonitor;
- mDumpManager = dumpManager;
mKeyguardInteractor = keyguardInteractor;
mPowerInteractor = powerInteractor;
}
@@ -222,7 +214,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
});
}
- mDumpManager.registerDumpable(getInstanceName(), this);
if (MigrateClocksToBlueprint.isEnabled()) {
startCoroutines(EmptyCoroutineContext.INSTANCE);
mView.setVisibility(View.GONE);
@@ -275,13 +266,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mKeyguardClockSwitchController.setShownOnSecondaryDisplay(true);
}
- /**
- * Called in notificationPanelViewController to avoid leak
- */
- public void onDestroy() {
- mDumpManager.unregisterDumpable(getInstanceName());
- }
-
/**
* Updates views on doze time tick.
*/
@@ -604,11 +588,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
return mKeyguardClockSwitchController.getClock();
}
- @Override
- public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- mView.dump(pw, args);
- }
-
String getInstanceName() {
return TAG + "#" + hashCode();
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index fff5e104a7ab..6e74c1ed385c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1306,10 +1306,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
/** Updates the StatusBarViewController and updates any that depend on it. */
public void updateStatusViewController() {
// Re-associate the KeyguardStatusViewController
- if (mKeyguardStatusViewController != null) {
- mKeyguardStatusViewController.onDestroy();
- }
-
if (MigrateClocksToBlueprint.isEnabled()) {
// Need a shared controller until mKeyguardStatusViewController can be removed from
// here, due to important state being set in that controller. Rebind in order to pick
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
index 07504c732bc2..2b4fc5bd5cc5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -26,7 +26,6 @@ import android.widget.FrameLayout;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.kosmos.KosmosJavaAdapter;
@@ -59,7 +58,6 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
@Mock protected KeyguardLogger mKeyguardLogger;
@Mock protected KeyguardStatusViewController mControllerMock;
@Mock protected ViewTreeObserver mViewTreeObserver;
- @Mock protected DumpManager mDumpManager;
protected FakeKeyguardRepository mFakeKeyguardRepository;
protected FakePowerRepository mFakePowerRepository;
@@ -90,7 +88,6 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
mKeyguardLogger,
mKosmos.getInteractionJankMonitor(),
deps.getKeyguardInteractor(),
- mDumpManager,
PowerInteractorFactory.create(
mFakePowerRepository
).getPowerInteractor()) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 0696a4b880d5..8e441a3db242 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -139,14 +139,6 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll
verify(mKeyguardClockSwitchController, times(0)).setSplitShadeEnabled(true);
}
- @Test
- public void correctlyDump() {
- mController.onInit();
- verify(mDumpManager).registerDumpable(eq(mController.getInstanceName()), eq(mController));
- mController.onDestroy();
- verify(mDumpManager, times(1)).unregisterDumpable(eq(mController.getInstanceName()));
- }
-
@Test
public void onInit_addsOnLayoutChangeListenerToClockSwitch() {
when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index c3cedf84a864..bf22a0c81167 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -479,7 +479,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mKeyguardLogger,
mKosmos.getInteractionJankMonitor(),
mKeyguardInteractor,
- mDumpManager,
mPowerInteractor));
when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 1d07bcae3f35..69478bbd0d44 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1234,6 +1234,10 @@ public class AccountManagerService
obsoleteAuthType.add(type);
// And delete it from the TABLE_META
accountsDb.deleteMetaByAuthTypeAndUid(type, uid);
+ } else if (knownUid != null && knownUid != uid) {
+ Slog.w(TAG, "authenticator no longer exist for type " + type);
+ obsoleteAuthType.add(type);
+ accountsDb.deleteMetaByAuthTypeAndUid(type, uid);
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d41de38ce2a8..46c963f009c7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4898,7 +4898,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (!mConstants.mEnableWaitForFinishAttachApplication) {
finishAttachApplicationInner(startSeq, callingUid, pid);
}
- maybeSendBootCompletedLocked(app);
+ maybeSendBootCompletedLocked(app, isRestrictedBackupMode);
} catch (Exception e) {
// We need kill the process group here. (b/148588589)
Slog.wtf(TAG, "Exception thrown during bind of " + app, e);
@@ -5143,7 +5143,7 @@ public class ActivityManagerService extends IActivityManager.Stub
* Send LOCKED_BOOT_COMPLETED and BOOT_COMPLETED to the package explicitly when unstopped,
* or when the package first starts in private space
*/
- private void maybeSendBootCompletedLocked(ProcessRecord app) {
+ private void maybeSendBootCompletedLocked(ProcessRecord app, boolean isRestrictedBackupMode) {
boolean sendBroadcast = false;
if (android.os.Flags.allowPrivateProfile()
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) {
@@ -5169,6 +5169,9 @@ public class ActivityManagerService extends IActivityManager.Stub
RESTRICTION_REASON_USAGE, "unknown", RESTRICTION_SOURCE_USER, 0L);
}
+ // Don't send BOOT_COMPLETED if currently in restricted backup mode
+ if (isRestrictedBackupMode) return;
+
if (!sendBroadcast) {
if (!android.content.pm.Flags.stayStopped()) return;
// Nothing to do if it wasn't previously stopped
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index e8394d43f266..336ba0526668 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -47,6 +47,7 @@ import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManagerInternal;
import android.hardware.devicestate.IDeviceStateManager;
import android.hardware.devicestate.IDeviceStateManagerCallback;
+import android.hardware.devicestate.feature.flags.Flags;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -968,16 +969,16 @@ public final class DeviceStateManagerService extends SystemService {
* @param callingPid Process ID that is requesting this state change
* @param state state that is being requested.
*/
- private void assertCanRequestDeviceState(int callingPid, int callingUid, int state) {
+ private void enforceRequestDeviceStatePermitted(int callingPid, int callingUid, int state) {
final boolean isTopApp = isTopApp(callingPid);
final boolean isForegroundApp = isForegroundApp(callingPid, callingUid);
final boolean isStateAvailableForAppRequests = isStateAvailableForAppRequests(state);
- final boolean canRequestState = isTopApp
+ final boolean isAllowedToRequestState = isTopApp
&& isForegroundApp
&& isStateAvailableForAppRequests;
- if (!canRequestState) {
+ if (!isAllowedToRequestState) {
getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
"Permission required to request device state, "
+ "or the call must come from the top app "
@@ -986,19 +987,29 @@ public final class DeviceStateManagerService extends SystemService {
}
/**
- * Checks if the process can control the device state. If the calling process ID is
- * not the top app, then check if this process holds the CONTROL_DEVICE_STATE permission.
+ * Checks if the process can cancel a device state request. If the calling process ID is not
+ * both the top app and foregrounded, verify that the calling process is in the foreground and
+ * that it matches the process ID and user ID that made the device state request. If neither are
+ * true, then check if this process holds the CONTROL_DEVICE_STATE permission.
*
* @param callingPid Process ID that is requesting this state change
* @param callingUid UID that is requesting this state change
*/
- private void assertCanControlDeviceState(int callingPid, int callingUid) {
+ private void enforceCancelDeviceStatePermitted(int callingPid, int callingUid) {
final boolean isTopApp = isTopApp(callingPid);
final boolean isForegroundApp = isForegroundApp(callingPid, callingUid);
- final boolean canControlState = isTopApp && isForegroundApp;
+ boolean isAllowedToControlState = isTopApp && isForegroundApp;
- if (!canControlState) {
+ if (Flags.deviceStateRequesterCancelState()) {
+ synchronized (mLock) {
+ isAllowedToControlState =
+ isTopApp || (isForegroundApp && doCallingIdsMatchOverrideRequestIdsLocked(
+ callingPid, callingUid));
+ }
+ }
+
+ if (!isAllowedToControlState) {
getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
"Permission required to request device state, "
+ "or the call must come from the top app.");
@@ -1032,6 +1043,16 @@ public final class DeviceStateManagerService extends SystemService {
return topApp != null && topApp.getPid() == callingPid;
}
+ /**
+ * Returns if the provided {@code callingPid} and {@code callingUid} match the same id's that
+ * requested the current device state override.
+ */
+ @GuardedBy("mLock")
+ private boolean doCallingIdsMatchOverrideRequestIdsLocked(int callingPid, int callingUid) {
+ OverrideRequest request = mActiveOverride.orElse(null);
+ return request != null && request.getPid() == callingPid && request.getUid() == callingUid;
+ }
+
private boolean isStateAvailableForAppRequests(int state) {
synchronized (mLock) {
return mDeviceStatesAvailableForAppRequests.contains(state);
@@ -1256,7 +1277,7 @@ public final class DeviceStateManagerService extends SystemService {
// Allow top processes to request a device state change
// If the calling process ID is not the top app, then we check if this process
// holds a permission to CONTROL_DEVICE_STATE
- assertCanRequestDeviceState(callingPid, callingUid, state);
+ enforceRequestDeviceStatePermitted(callingPid, callingUid, state);
if (token == null) {
throw new IllegalArgumentException("Request token must not be null.");
@@ -1281,7 +1302,7 @@ public final class DeviceStateManagerService extends SystemService {
// Allow top processes to cancel a device state change
// If the calling process ID is not the top app, then we check if this process
// holds a permission to CONTROL_DEVICE_STATE
- assertCanControlDeviceState(callingPid, callingUid);
+ enforceCancelDeviceStatePermitted(callingPid, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index 9ef2e12e55c5..f6d9dc29d330 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -648,6 +648,21 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
Slog.w(TAG, "Failed to send connected event", ex);
}
}
+
+ @Override
+ public void onDisconnected(
+ @NonNull IOnDeviceSandboxedInferenceService service) {
+ ensureRemoteIntelligenceServiceInitialized();
+ mRemoteOnDeviceIntelligenceService.run(
+ IOnDeviceIntelligenceService::notifyInferenceServiceDisconnected);
+ }
+
+ @Override
+ public void onBinderDied() {
+ ensureRemoteIntelligenceServiceInitialized();
+ mRemoteOnDeviceIntelligenceService.run(
+ IOnDeviceIntelligenceService::notifyInferenceServiceDisconnected);
+ }
});
}
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index b079fed65d5d..f2a7cb7d3665 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -686,6 +686,9 @@ final class InstallPackageHelper {
(installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
final boolean fullApp =
(installFlags & PackageManager.INSTALL_FULL_APP) != 0;
+ final boolean isPackageDeviceAdmin = mPm.isPackageDeviceAdmin(packageName, userId);
+ final boolean isProtectedPackage = mPm.mProtectedPackages != null
+ && mPm.mProtectedPackages.isPackageStateProtected(userId, packageName);
// writer
synchronized (mPm.mLock) {
@@ -694,7 +697,8 @@ final class InstallPackageHelper {
if (pkgSetting == null || pkgSetting.getPkg() == null) {
return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender);
}
- if (instantApp && (pkgSetting.isSystem() || pkgSetting.isUpdatedSystemApp())) {
+ if (instantApp && (pkgSetting.isSystem() || pkgSetting.isUpdatedSystemApp()
+ || isPackageDeviceAdmin || isProtectedPackage)) {
return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender);
}
if (!snapshot.canViewInstantApps(callingUid, UserHandle.getUserId(callingUid))) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e6d81324efa1..365fa950a2d9 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -98,6 +98,7 @@ import android.app.ProfilerInfo;
import android.app.WaitResult;
import android.app.WindowConfiguration;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince;
import android.content.IIntentSender;
import android.content.Intent;
@@ -179,7 +180,7 @@ class ActivityStarter {
* Feature flag for go/activity-security rules
*/
@ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @Disabled
static final long ASM_RESTRICTIONS = 230590090L;
private final ActivityTaskManagerService mService;
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 30f2d0d64d13..6abef8b9a048 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.content.ClipDescription.EXTRA_HIDE_DRAG_SOURCE_TASK_ID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.View.DRAG_FLAG_GLOBAL;
import static android.view.View.DRAG_FLAG_GLOBAL_SAME_APPLICATION;
@@ -217,6 +218,11 @@ class DragDropController {
mDragState.mToken = dragToken;
mDragState.mDisplayContent = displayContent;
mDragState.mData = data;
+ mDragState.mCallingTaskIdToHide = shouldMoveCallingTaskToBack(callingWin,
+ flags);
+ if (DEBUG_DRAG) {
+ Slog.d(TAG_WM, "Calling task to hide=" + mDragState.mCallingTaskIdToHide);
+ }
if ((flags & View.DRAG_FLAG_ACCESSIBILITY_ACTION) == 0) {
final Display display = displayContent.getDisplay();
@@ -363,6 +369,23 @@ class DragDropController {
}
}
+ /**
+ * If the calling window's task should be hidden for the duration of the drag, this returns the
+ * task id of the task (or -1 otherwise).
+ */
+ private int shouldMoveCallingTaskToBack(WindowState callingWin, int flags) {
+ if ((flags & View.DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START) == 0) {
+ // Not requested by the app
+ return -1;
+ }
+ final ActivityRecord callingActivity = callingWin.getActivityRecord();
+ if (callingActivity == null || callingActivity.getTask() == null) {
+ // Not an activity
+ return -1;
+ }
+ return callingActivity.getTask().mTaskId;
+ }
+
/**
* Notifies the unhandled drag listener if needed.
* @return whether the listener was notified and subsequent drag completion should be deferred
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index e3827aa86d9e..5a1279024eb5 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.content.ClipDescription.EXTRA_HIDE_DRAG_SOURCE_TASK_ID;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
@@ -48,6 +49,7 @@ import android.graphics.Rect;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
@@ -117,6 +119,8 @@ class DragState {
InputInterceptor mInputInterceptor;
ArrayList<WindowState> mNotifiedWindows;
boolean mDragInProgress;
+ // Set to non -1 value if a valid app requests DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START
+ int mCallingTaskIdToHide;
/**
* Whether if animation is completed. Needs to be volatile to update from the animation thread
* without having a WM lock.
@@ -320,12 +324,12 @@ class DragState {
}
}
final boolean targetInterceptsGlobalDrag = targetInterceptsGlobalDrag(touchedWin);
- return obtainDragEvent(DragEvent.ACTION_DROP, x, y, mData,
+ return obtainDragEvent(DragEvent.ACTION_DROP, x, y, mDataDescription, mData,
/* includeDragSurface= */ targetInterceptsGlobalDrag,
/* includeDragFlags= */ targetInterceptsGlobalDrag,
dragAndDropPermissions);
} else {
- return obtainDragEvent(DragEvent.ACTION_DROP, x, y, mData,
+ return obtainDragEvent(DragEvent.ACTION_DROP, x, y, mDataDescription, mData,
/* includeDragSurface= */ includePrivateInfo,
/* includeDragFlags= */ includePrivateInfo,
null /* dragAndDropPermissions */);
@@ -527,11 +531,24 @@ class DragState {
Slog.d(TAG_WM, "Sending DRAG_STARTED to new window " + newWin);
}
// Only allow the extras to be dispatched to a global-intercepting drag target
- ClipData data = interceptsGlobalDrag ? mData.copyForTransferWithActivityInfo() : null;
+ ClipData data = null;
+ if (interceptsGlobalDrag) {
+ data = mData.copyForTransferWithActivityInfo();
+ PersistableBundle extras = data.getDescription().getExtras() != null
+ ? data.getDescription().getExtras()
+ : new PersistableBundle();
+ extras.putInt(EXTRA_HIDE_DRAG_SOURCE_TASK_ID, mCallingTaskIdToHide);
+ // Note that setting extras always copies the bundle
+ data.getDescription().setExtras(extras);
+ if (DEBUG_DRAG) {
+ Slog.d(TAG_WM, "Adding EXTRA_HIDE_DRAG_SOURCE_TASK_ID=" + mCallingTaskIdToHide);
+ }
+ }
+ ClipDescription description = data != null ? data.getDescription() : mDataDescription;
DragEvent event = obtainDragEvent(DragEvent.ACTION_DRAG_STARTED,
newWin.translateToWindowX(touchX), newWin.translateToWindowY(touchY),
- data, false /* includeDragSurface */, true /* includeDragFlags */,
- null /* dragAndDropPermission */);
+ description, data, false /* includeDragSurface */,
+ true /* includeDragFlags */, null /* dragAndDropPermission */);
try {
newWin.mClient.dispatchDragEvent(event);
// track each window that we've notified that the drag is starting
@@ -700,37 +717,51 @@ class DragState {
return mDragInProgress;
}
- private DragEvent obtainDragEvent(int action, float x, float y, ClipData data,
- boolean includeDragSurface, boolean includeDragFlags,
+ private DragEvent obtainDragEvent(int action, float x, float y, ClipDescription description,
+ ClipData data, boolean includeDragSurface, boolean includeDragFlags,
IDragAndDropPermissions dragAndDropPermissions) {
return DragEvent.obtain(action, x, y, mThumbOffsetX, mThumbOffsetY,
includeDragFlags ? mFlags : 0,
- null /* localState */, mDataDescription, data,
+ null /* localState */, description, data,
includeDragSurface ? mSurfaceControl : null,
dragAndDropPermissions, false /* result */);
}
private ValueAnimator createReturnAnimationLocked() {
- final ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(
- PropertyValuesHolder.ofFloat(
- ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX,
- mOriginalX - mThumbOffsetX),
- PropertyValuesHolder.ofFloat(
- ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY,
- mOriginalY - mThumbOffsetY),
- PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale,
- mAnimatedScale),
- PropertyValuesHolder.ofFloat(
- ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, mOriginalAlpha / 2));
-
- final float translateX = mOriginalX - mCurrentX;
- final float translateY = mOriginalY - mCurrentY;
- // Adjust the duration to the travel distance.
- final double travelDistance = Math.sqrt(translateX * translateX + translateY * translateY);
- final double displayDiagonal =
- Math.sqrt(mDisplaySize.x * mDisplaySize.x + mDisplaySize.y * mDisplaySize.y);
- final long duration = MIN_ANIMATION_DURATION_MS + (long) (travelDistance / displayDiagonal
- * (MAX_ANIMATION_DURATION_MS - MIN_ANIMATION_DURATION_MS));
+ final ValueAnimator animator;
+ final long duration;
+ if (mCallingTaskIdToHide != -1) {
+ animator = ValueAnimator.ofPropertyValuesHolder(
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_X, mCurrentX, mCurrentX),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_Y, mCurrentY, mCurrentY),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale,
+ mAnimatedScale),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0f));
+ duration = MIN_ANIMATION_DURATION_MS;
+ } else {
+ animator = ValueAnimator.ofPropertyValuesHolder(
+ PropertyValuesHolder.ofFloat(
+ ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX,
+ mOriginalX - mThumbOffsetX),
+ PropertyValuesHolder.ofFloat(
+ ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY,
+ mOriginalY - mThumbOffsetY),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale,
+ mAnimatedScale),
+ PropertyValuesHolder.ofFloat(
+ ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, mOriginalAlpha / 2));
+
+ final float translateX = mOriginalX - mCurrentX;
+ final float translateY = mOriginalY - mCurrentY;
+ // Adjust the duration to the travel distance.
+ final double travelDistance = Math.sqrt(
+ translateX * translateX + translateY * translateY);
+ final double displayDiagonal =
+ Math.sqrt(mDisplaySize.x * mDisplaySize.x + mDisplaySize.y * mDisplaySize.y);
+ duration = MIN_ANIMATION_DURATION_MS + (long) (travelDistance / displayDiagonal
+ * (MAX_ANIMATION_DURATION_MS - MIN_ANIMATION_DURATION_MS));
+ }
+
final AnimationListener listener = new AnimationListener();
animator.setDuration(duration);
animator.setInterpolator(mCubicEaseOutInterpolator);
@@ -742,13 +773,24 @@ class DragState {
}
private ValueAnimator createCancelAnimationLocked() {
- final ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(
- PropertyValuesHolder.ofFloat(
- ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX, mCurrentX),
- PropertyValuesHolder.ofFloat(
- ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY, mCurrentY),
- PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale, 0),
- PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0));
+ final ValueAnimator animator;
+ if (mCallingTaskIdToHide != -1) {
+ animator = ValueAnimator.ofPropertyValuesHolder(
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_X, mCurrentX, mCurrentX),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_Y, mCurrentY, mCurrentY),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale,
+ mAnimatedScale),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0f));
+ } else {
+ animator = ValueAnimator.ofPropertyValuesHolder(
+ PropertyValuesHolder.ofFloat(
+ ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX, mCurrentX),
+ PropertyValuesHolder.ofFloat(
+ ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY, mCurrentY),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale, 0),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0));
+ }
+
final AnimationListener listener = new AnimationListener();
animator.setDuration(MIN_ANIMATION_DURATION_MS);
animator.setInterpolator(mCubicEaseOutInterpolator);
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index f2dc55f38bc3..2d961e1f0766 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -438,7 +438,10 @@ public class SafeActivityOptions {
return taskDisplayArea;
}
- private boolean isAssistant(ActivityTaskManagerService atmService, int callingUid) {
+ /**
+ * Returns whether the given UID caller is the assistant.
+ */
+ public static boolean isAssistant(ActivityTaskManagerService atmService, int callingUid) {
if (atmService.mActiveVoiceInteractionServiceComponent == null) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 0562cd979cb8..4cd27bcdb616 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -377,7 +377,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final int callingPid = Binder.getCallingPid();
// Validate and resolve ClipDescription data before clearing the calling identity
validateAndResolveDragMimeTypeExtras(data, callingUid, callingPid, mPackageName);
- validateDragFlags(flags);
+ validateDragFlags(flags, callingUid);
final long ident = Binder.clearCallingIdentity();
try {
return mDragDropController.performDrag(mPid, mUid, window, flags, surface, touchSource,
@@ -403,12 +403,17 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
* Validates the given drag flags.
*/
@VisibleForTesting
- void validateDragFlags(int flags) {
+ void validateDragFlags(int flags, int callingUid) {
if ((flags & View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION) != 0) {
if (!mCanStartTasksFromRecents) {
throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission");
}
}
+ if ((flags & View.DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START) != 0) {
+ if (!SafeActivityOptions.isAssistant(mService.mAtmService, callingUid)) {
+ throw new SecurityException("Caller is not the assistant");
+ }
+ }
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 7faf2aacc0bc..8cdb574a967b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -497,7 +497,8 @@ public class DragDropControllerTests extends WindowTestsBase {
public void testValidateFlags() {
final Session session = getTestSession();
try {
- session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION);
+ session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION,
+ 0 /* callingUid */);
fail("Expected failure without permission");
} catch (SecurityException e) {
// Expected failure
@@ -510,7 +511,8 @@ public class DragDropControllerTests extends WindowTestsBase {
.checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
final Session session = createTestSession(mAtm);
try {
- session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION);
+ session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION,
+ 0 /* callingUid */);
// Expected pass
} catch (SecurityException e) {
fail("Expected no failure with permission");
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 175a09db54e3..ae89996c8d26 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -78,9 +78,9 @@ import android.os.storage.StorageVolume;
import android.provider.Settings;
import android.service.usb.UsbDeviceManagerProto;
import android.service.usb.UsbHandlerProto;
+import android.text.TextUtils;
import android.util.Pair;
import android.util.Slog;
-import android.text.TextUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -838,7 +838,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
}
}
- private void notifyAccessoryModeExit(int operationId) {
+ protected void notifyAccessoryModeExit(int operationId) {
// make sure accessory mode is off
// and restore default functions
Slog.d(TAG, "exited USB accessory mode");
@@ -2271,8 +2271,13 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
*/
operationId = sUsbOperationCount.incrementAndGet();
if (msg.arg1 != 1) {
- // Set this since default function may be selected from Developer options
- setEnabledFunctions(mScreenUnlockedFunctions, false, operationId);
+ if (mCurrentFunctions == UsbManager.FUNCTION_ACCESSORY) {
+ notifyAccessoryModeExit(operationId);
+ } else {
+ // Set this since default function may be selected from Developer
+ // options
+ setEnabledFunctions(mScreenUnlockedFunctions, false, operationId);
+ }
}
break;
case MSG_GADGET_HAL_REGISTERED:
frameworks/native
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index ff6b558d41..269936858a 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -62,7 +62,7 @@ public:
status_t setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands commands, int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index af91bb3ae2..5db539497c 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1059,7 +1059,8 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
uncacheBuffer.token = BufferCache::getInstance().getToken();
uncacheBuffer.id = cacheId;
Vector<ComposerState> composerStates;
- status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, {},
+ Vector<DisplayState> displayStates;
+ status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, displayStates,
ISurfaceComposer::eOneWay,
Transaction::getDefaultApplyToken(), {}, systemTime(),
true, {uncacheBuffer}, false, {}, generateId(), {});
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index eb4a802c17..1ecc216dff 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -112,7 +112,7 @@ public:
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
virtual status_t setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 43cd0f8a7f..5e91088378 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -636,7 +636,7 @@ public:
status_t setTransactionState(
const FrameTimelineInfo& /*frameTimelineInfo*/, Vector<ComposerState>& /*state*/,
- const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
+ Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
const sp<IBinder>& /*applyToken*/, InputWindowCommands /*inputWindowCommands*/,
int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
const std::vector<client_cache_t>& /*cachedBuffer*/, bool /*hasListenerCallbacks*/,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index fcead9ff6e..2d90d4041a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -5182,7 +5182,7 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const layer_state_t& state, size_t nu
status_t SurfaceFlinger::setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
@@ -5197,7 +5197,7 @@ status_t SurfaceFlinger::setTransactionState(
composerState.state.sanitize(permissions);
}
- for (DisplayState display : displays) {
+ for (DisplayState& display : displays) {
display.sanitize(permissions);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a3534b582c..92bd12527d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -559,7 +559,7 @@ private:
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const;
status_t setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index ebe11fb0f3..d355e720d1 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -26,6 +26,7 @@
#include <private/android_filesystem_config.h>
#include <private/gui/ComposerServiceAIDL.h>
#include <ui/DisplayMode.h>
+#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
#include <utils/String8.h>
#include <functional>
@@ -276,7 +277,7 @@ TEST_F(CredentialsTest, CreateDisplayTest) {
TEST_F(CredentialsTest, CaptureLayersTest) {
setupBackgroundSurface();
sp<GraphicBuffer> outBuffer;
- std::function<status_t()> condition = [=]() {
+ std::function<status_t()> condition = [=, this]() {
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mBGSurfaceControl->getHandle();
captureArgs.sourceCrop = {0, 0, 1, 1};
@@ -396,6 +397,56 @@ TEST_F(CredentialsTest, TransactionPermissionTest) {
}
}
+TEST_F(CredentialsTest, DisplayTransactionPermissionTest) {
+ const auto display = getFirstDisplayToken();
+
+ ui::DisplayState displayState;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ const ui::Rotation initialOrientation = displayState.orientation;
+
+ // Set display orientation from an untrusted process. This should fail silently.
+ {
+ UIDFaker f{AID_BIN};
+ Transaction transaction;
+ Rect layerStackRect;
+ Rect displayRect;
+ transaction.setDisplayProjection(display, initialOrientation + ui::ROTATION_90,
+ layerStackRect, displayRect);
+ transaction.apply(/*synchronous=*/true);
+ }
+
+ // Verify that the display orientation did not change.
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ ASSERT_EQ(initialOrientation, displayState.orientation);
+
+ // Set display orientation from a trusted process.
+ {
+ UIDFaker f{AID_SYSTEM};
+ Transaction transaction;
+ Rect layerStackRect;
+ Rect displayRect;
+ transaction.setDisplayProjection(display, initialOrientation + ui::ROTATION_90,
+ layerStackRect, displayRect);
+ transaction.apply(/*synchronous=*/true);
+ }
+
+ // Verify that the display orientation did change.
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ ASSERT_EQ(initialOrientation + ui::ROTATION_90, displayState.orientation);
+
+ // Reset orientation
+ {
+ UIDFaker f{AID_SYSTEM};
+ Transaction transaction;
+ Rect layerStackRect;
+ Rect displayRect;
+ transaction.setDisplayProjection(display, initialOrientation, layerStackRect, displayRect);
+ transaction.apply(/*synchronous=*/true);
+ }
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ ASSERT_EQ(initialOrientation, displayState.orientation);
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 4197cbd271..b5b36bea19 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -528,7 +528,7 @@ public:
auto setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
bool hasListenerCallbacks, std::vector<ListenerCallbacks>& listenerCallbacks,
frameworks/opt/telephony
hardware/google/graphics/common
diff --git a/include/displaycolor/displaycolor.h b/include/displaycolor/displaycolor.h
index cd943f3..b25d93b 100644
--- a/include/displaycolor/displaycolor.h
+++ b/include/displaycolor/displaycolor.h
@@ -346,7 +346,8 @@ struct DisplayScene {
dbv == rhs.dbv &&
refresh_rate == rhs.refresh_rate &&
operation_rate == rhs.operation_rate &&
- hdr_layer_state == rhs.hdr_layer_state;
+ hdr_layer_state == rhs.hdr_layer_state &&
+ temperature == rhs.temperature;
}
bool operator!=(const DisplayScene &rhs) const {
return !(*this == rhs);
@@ -388,6 +389,9 @@ struct DisplayScene {
/// operation rate to switch between hs/ns mode
uint32_t operation_rate = 120;
+ /// display temperature in degrees Celsius
+ uint32_t temperature = UINT_MAX;
+
/// hdr layer state on screen
HdrLayerState hdr_layer_state = HdrLayerState::kHdrNone;
};
diff --git a/libhwc2.1/libdevice/ExynosDisplay.h b/libhwc2.1/libdevice/ExynosDisplay.h
index 2db9275..1972de6 100644
--- a/libhwc2.1/libdevice/ExynosDisplay.h
+++ b/libhwc2.1/libdevice/ExynosDisplay.h
@@ -1377,6 +1377,8 @@ class ExynosDisplay {
virtual int32_t setFixedTe2Rate(const int __unused rateHz) { return NO_ERROR; }
+ virtual int32_t setDisplayTemperature(const int __unused temperature) { return NO_ERROR; }
+
virtual int32_t registerRefreshRateChangeListener(
std::shared_ptr<RefreshRateChangeListener> listener) {
return NO_ERROR;
diff --git a/libhwc2.1/libhwcService/ExynosHWCService.cpp b/libhwc2.1/libhwcService/ExynosHWCService.cpp
index 42dadb6..5113a8e 100644
--- a/libhwc2.1/libhwcService/ExynosHWCService.cpp
+++ b/libhwc2.1/libhwcService/ExynosHWCService.cpp
@@ -601,4 +601,16 @@ int32_t ExynosHWCService::setFixedTe2Rate(uint32_t displayId, int32_t rateHz) {
return -EINVAL;
}
+int32_t ExynosHWCService::setDisplayTemperature(uint32_t displayId, int32_t temperature) {
+ ALOGI("ExynosHWCService::%s() displayID(%u) temperature(%d)", __func__, displayId, temperature);
+
+ auto display = mHWCCtx->device->getDisplay(displayId);
+
+ if (display != nullptr) {
+ display->setDisplayTemperature(temperature);
+ }
+
+ return NO_ERROR;
+}
+
} //namespace android
diff --git a/libhwc2.1/libhwcService/ExynosHWCService.h b/libhwc2.1/libhwcService/ExynosHWCService.h
index 449a829..0ef57f2 100644
--- a/libhwc2.1/libhwcService/ExynosHWCService.h
+++ b/libhwc2.1/libhwcService/ExynosHWCService.h
@@ -94,6 +94,7 @@ public:
const std::vector<std::pair<uint32_t, uint32_t>>& __unused
settings) override;
virtual int32_t setFixedTe2Rate(uint32_t displayId, int32_t rateHz);
+ virtual int32_t setDisplayTemperature(uint32_t displayId, int32_t temperature);
private:
friend class Singleton<ExynosHWCService>;
diff --git a/libhwc2.1/libhwcService/IExynosHWC.cpp b/libhwc2.1/libhwcService/IExynosHWC.cpp
index 87bcdec..e89ae8d 100644
--- a/libhwc2.1/libhwcService/IExynosHWC.cpp
+++ b/libhwc2.1/libhwcService/IExynosHWC.cpp
@@ -82,6 +82,7 @@ enum {
SET_PRESENT_TIMEOUT_PARAMETERS = 1016,
SET_PRESENT_TIMEOUT_CONTROLLER = 1017,
SET_FIXED_TE2_RATE = 1018,
+ SET_DISPLAY_TEMPERATURE = 1019,
};
class BpExynosHWCService : public BpInterface<IExynosHWCService> {
@@ -584,6 +585,15 @@ public:
if (result) ALOGE("SET_FIXED_TE2_RATE transact error(%d)", result);
return result;
}
+ virtual int32_t setDisplayTemperature(uint32_t displayId, int32_t temperature) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IExynosHWCService::getInterfaceDescriptor());
+ data.writeUint32(displayId);
+ data.writeInt32(temperature);
+ int result = remote()->transact(SET_DISPLAY_TEMPERATURE, data, &reply);
+ if (result) ALOGE("SET_DISPLAY_TEMPERATURE transact error(%d)", result);
+ return result;
+ }
};
IMPLEMENT_META_INTERFACE(ExynosHWCService, "android.hal.ExynosHWCService");
@@ -925,6 +935,13 @@ status_t BnExynosHWCService::onTransact(
return setFixedTe2Rate(displayId, rateHz);
} break;
+ case SET_DISPLAY_TEMPERATURE: {
+ CHECK_INTERFACE(IExynosHWCService, data, reply);
+ uint32_t displayId = data.readUint32();
+ int32_t temperature = data.readInt32();
+ return setDisplayTemperature(displayId, temperature);
+ } break;
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libhwc2.1/libhwcService/IExynosHWC.h b/libhwc2.1/libhwcService/IExynosHWC.h
index 484f141..fe7e0ce 100644
--- a/libhwc2.1/libhwcService/IExynosHWC.h
+++ b/libhwc2.1/libhwcService/IExynosHWC.h
@@ -86,6 +86,7 @@ public:
uint32_t displayId, int timeoutNs,
const std::vector<std::pair<uint32_t, uint32_t>>& settings) = 0;
virtual int32_t setFixedTe2Rate(uint32_t displayId, int32_t rateHz) = 0;
+ virtual int32_t setDisplayTemperature(uint32_t displayId, int32_t temperature) = 0;
};
/* Native Interface */
diff --git a/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.cpp b/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.cpp
index 99767a2..34d89f3 100644
--- a/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.cpp
+++ b/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.cpp
@@ -244,6 +244,7 @@ ExynosPrimaryDisplay::ExynosPrimaryDisplay(uint32_t index, ExynosDevice* device,
ALOGI("%s(): refresh control is not supported", __func__);
}
}
+ mIsDisplayTempMonitorSupported = initDisplayTempMonitor(displayTypeIdentifier);
}
// Allow to enable dynamic recomposition after every power on
@@ -307,6 +308,14 @@ ExynosPrimaryDisplay::~ExynosPrimaryDisplay()
if (mDisplayNeedHandleIdleExitOfs.is_open()) {
mDisplayNeedHandleIdleExitOfs.close();
}
+
+ if (mIsDisplayTempMonitorSupported) {
+ mTMLoopStatus.store(false, std::memory_order_relaxed);
+ mTMCondition.notify_one();
+ if (mTMThread.joinable()) {
+ mTMThread.join();
+ }
+ }
}
void ExynosPrimaryDisplay::setDDIScalerEnable(int width, int height) {
@@ -668,6 +677,9 @@ int32_t ExynosPrimaryDisplay::setPowerMode(int32_t mode) {
mRefreshRateDelayNanos);
}
}
+
+ checkTemperatureMonitorThread(mPowerModeState.has_value() && mode == HWC2_POWER_MODE_ON);
+
return res;
}
@@ -1325,6 +1337,11 @@ int32_t ExynosPrimaryDisplay::setFixedTe2Rate(const int targetTe2RateHz) {
}
}
+int32_t ExynosPrimaryDisplay::setDisplayTemperature(const int temperature) {
+ mDisplayTemperature = temperature;
+ return HWC2_ERROR_UNSUPPORTED;
+}
+
int32_t ExynosPrimaryDisplay::setMinIdleRefreshRate(const int targetFps,
const RrThrottleRequester requester) {
if (targetFps < 0) {
@@ -1512,6 +1529,9 @@ void ExynosPrimaryDisplay::dump(String8 &result) {
for (uint32_t i = 0; i < toUnderlying(RrThrottleRequester::MAX); i++) {
result.appendFormat("\t[%u] vote to %" PRId64 " ns\n", i, mRrThrottleNanos[i]);
}
+ if (mIsDisplayTempMonitorSupported) {
+ result.appendFormat("Temperature : %d°C\n", mDisplayTemperature);
+ }
result.appendFormat("\n");
}
@@ -1715,3 +1735,111 @@ int32_t ExynosPrimaryDisplay::registerRefreshRateChangeListener(
return -EINVAL;
}
}
+
+// TODO(b/355579338): to create a dedicated class DisplayTemperatureMonitor
+bool ExynosPrimaryDisplay::initDisplayTempMonitor(const std::string& display) {
+ mDisplayTempInterval = property_get_int32(kDisplayTempIntervalSec, 0);
+
+ if (mDisplayTempInterval <= 0) {
+ ALOGD("%s: Invalid display temperature interval: %d", __func__, mDisplayTempInterval);
+ return false;
+ }
+
+ auto propertyName = getPropertyDisplayTemperatureStr(display);
+
+ char value[PROPERTY_VALUE_MAX];
+ auto ret = property_get(propertyName.c_str(), value, "");
+
+ if (ret <= 0) {
+ ALOGD("%s: Display temperature property values is empty", __func__);
+ return false;
+ }
+
+ mDisplayTempSysfsNode = String8(value);
+ return true;
+}
+
+int32_t ExynosPrimaryDisplay::getDisplayTemperature() {
+ DISPLAY_ATRACE_CALL();
+
+ if (mDisplayTempSysfsNode.empty()) {
+ ALOGE("%s: Display temp sysfs node string is empty", __func__);
+ return UINT_MAX;
+ }
+
+ int32_t temperature;
+ std::ifstream ifs(mDisplayTempSysfsNode.c_str());
+
+ if (!ifs.is_open()) {
+ ALOGE("%s: Unable to open node '%s', error = %s", __func__, mDisplayTempSysfsNode.c_str(),
+ strerror(errno));
+ return UINT_MAX;
+ }
+
+ if (!(ifs >> temperature) || !ifs.good()) {
+ ALOGE("%s: Unable to read node '%s', error = %s", __func__, mDisplayTempSysfsNode.c_str(),
+ strerror(errno));
+ }
+
+ ifs.close();
+ return temperature / 1000;
+}
+
+bool ExynosPrimaryDisplay::isTemperatureMonitorThreadRunning() {
+ android_atomic_acquire_load(&mTMThreadStatus);
+ return (mTMThreadStatus > 0);
+}
+
+void ExynosPrimaryDisplay::checkTemperatureMonitorThread(bool shouldRun) {
+ ATRACE_CALL();
+ if (!mIsDisplayTempMonitorSupported) {
+ return;
+ }
+
+ // If thread was destroyed, create thread and run.
+ if (!isTemperatureMonitorThreadRunning()) {
+ if (shouldRun) {
+ temperatureMonitorThreadCreate();
+ return;
+ }
+ } else {
+ // if screen state changed make the thread suspend/resume.
+ {
+ std::lock_guard<std::mutex> lock(mThreadMutex);
+ if (mTMLoopStatus != shouldRun) {
+ mTMLoopStatus = shouldRun;
+ mTMCondition.notify_one();
+ }
+ }
+ }
+}
+
+void ExynosPrimaryDisplay::temperatureMonitorThreadCreate() {
+ mTMLoopStatus.store(false, std::memory_order_relaxed);
+
+ ALOGI("Creating monitor display temperature thread");
+ mTMLoopStatus.store(true, std::memory_order_relaxed);
+ mTMThread = std::thread(&ExynosPrimaryDisplay::temperatureMonitorThreadLoop, this);
+ mTMCondition.notify_one();
+}
+
+void* ExynosPrimaryDisplay::temperatureMonitorThreadLoop() {
+ android_atomic_inc(&mTMThreadStatus);
+ while (true) {
+ std::unique_lock<std::mutex> lock(mThreadMutex);
+ mTMCondition.wait(lock, [this] { return mTMLoopStatus.load(std::memory_order_relaxed); });
+
+ mDisplayTemperature = getDisplayTemperature();
+ if (mDisplayTemperature == UINT_MAX) {
+ ALOGE("%s: Failed to get display temperature", LOG_TAG);
+ } else {
+ ALOGI("Display Temperature : %d°C", mDisplayTemperature);
+ }
+
+ // Wait for the specified interval or until the thread is suspended
+ mTMCondition.wait_for(lock, std::chrono::seconds(mDisplayTempInterval),
+ [this] { return !mTMLoopStatus.load(std::memory_order_relaxed); });
+ }
+ android_atomic_dec(&mTMThreadStatus);
+ return NULL;
+}
diff --git a/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.h b/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.h
index 1489284..95fd5bb 100644
--- a/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.h
+++ b/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.h
@@ -20,6 +20,7 @@
#include "../libdevice/ExynosDisplay.h"
#include "../libvrr/VariableRefreshRateController.h"
+#include <cutils/properties.h>
using android::hardware::graphics::composer::PresentListener;
using android::hardware::graphics::composer::VariableRefreshRateController;
@@ -81,6 +82,8 @@ class ExynosPrimaryDisplay : public ExynosDisplay {
virtual int32_t setFixedTe2Rate(const int rateHz) override;
+ virtual int32_t setDisplayTemperature(const int temperatue) override;
+
const std::string& getPanelName() final;
int32_t notifyExpectedPresent(int64_t timestamp, int32_t frameIntervalNs) override;
@@ -120,10 +123,13 @@ class ExynosPrimaryDisplay : public ExynosDisplay {
virtual bool isVrrSupported() const override { return mXrrSettings.versionInfo.isVrr(); }
uint32_t mRcdId = -1;
+ uint32_t getDisplayTemperatue() { return mDisplayTemperature; };
private:
static constexpr const char* kDisplayCalFilePath = "/mnt/vendor/persist/display/";
static constexpr const char* kPanelGammaCalFilePrefix = "gamma_calib_data";
+ static constexpr const char* kDisplayTempIntervalSec =
+ "ro.vendor.display.read_temp_interval";
enum PanelGammaSource currentPanelGammaSource = PanelGammaSource::GAMMA_DEFAULT;
bool checkLhbmMode(bool status, nsecs_t timoutNs);
@@ -148,6 +154,26 @@ class ExynosPrimaryDisplay : public ExynosDisplay {
int32_t setLhbmDisplayConfigLocked(uint32_t peakRate);
void restoreLhbmDisplayConfigLocked();
+
+ // monitor display thermal temperature
+ int32_t getDisplayTemperature();
+ bool initDisplayTempMonitor(const std::string& display);
+ bool isTemperatureMonitorThreadRunning();
+ void checkTemperatureMonitorThread(bool shouldRun);
+ void temperatureMonitorThreadCreate();
+ void* temperatureMonitorThreadLoop();
+ bool mIsDisplayTempMonitorSupported = false;
+ volatile int32_t mTMThreadStatus;
+ std::atomic<bool> mTMLoopStatus;
+ std::condition_variable mTMCondition;
+ std::thread mTMThread;
+ std::mutex mThreadMutex;
+ int32_t mDisplayTempInterval;
+ String8 mDisplayTempSysfsNode;
+ std::string getPropertyDisplayTemperatureStr(const std::string& display) {
+ return "ro.vendor." + display + "." + getPanelName() + ".temperature_path";
+ }
+
void onConfigChange(int configId);
// LHBM
@@ -207,6 +233,7 @@ class ExynosPrimaryDisplay : public ExynosDisplay {
XrrSettings_t mXrrSettings;
std::shared_ptr<VariableRefreshRateController> mVariableRefreshRateController;
+ uint32_t mDisplayTemperature = UINT_MAX;
};
#endif
hardware/google/graphics/gs101
diff --git a/libhwc2.1/libcolormanager/ColorManager.cpp b/libhwc2.1/libcolormanager/ColorManager.cpp
index e9590e7..df6dc7c 100644
--- a/libhwc2.1/libcolormanager/ColorManager.cpp
+++ b/libhwc2.1/libcolormanager/ColorManager.cpp
@@ -290,6 +290,7 @@ int32_t ColorManager::updateColorConversionInfo() {
displayScene.lhbm_on = false;
displayScene.hdr_layer_state = displaycolor::HdrLayerState::kHdrNone;
displayScene.dbv = 1000;
+ displayScene.refresh_rate = std::round(displayScene.refresh_rate);
if (brightnessController) {
displayScene.force_hdr = brightnessController->isDimSdr();
diff --git a/libhwc2.1/libmaindisplay/ExynosPrimaryDisplayModule.cpp b/libhwc2.1/libmaindisplay/ExynosPrimaryDisplayModule.cpp
index d08ee3d..2f86070 100644
--- a/libhwc2.1/libmaindisplay/ExynosPrimaryDisplayModule.cpp
+++ b/libhwc2.1/libmaindisplay/ExynosPrimaryDisplayModule.cpp
@@ -260,6 +260,7 @@ int32_t ExynosPrimaryDisplayModule::updatePresentColorConversionInfo()
getDisplaySceneInfo().displayScene.lhbm_on = mBrightnessController->isLhbmOn();
getDisplaySceneInfo().displayScene.dbv = mBrightnessController->getBrightnessLevel();
+ getDisplaySceneInfo().displayScene.temperature = getDisplayTemperatue();
const DisplayType display = getDcDisplayType();
if ((ret = displayColorInterface->UpdatePresent(display, getDisplaySceneInfo().displayScene)) !=
0) {
manifest
diff --git a/default.xml b/default.xml
index de3b2080f..e5678717e 100644
--- a/default.xml
+++ b/default.xml
@@ -4,11 +4,11 @@
<remote name="aosp"
fetch=".."
review="https://android-review.googlesource.com/" />
- <default revision="refs/tags/android-15.0.0_r3"
+ <default revision="refs/tags/android-15.0.0_r4"
remote="aosp"
sync-j="4" />
- <superproject name="platform/superproject" remote="aosp" revision="android-15.0.0_r3"/>
+ <superproject name="platform/superproject" remote="aosp" revision="android-15.0.0_r4"/>
<contactinfo bugurl="go/repo-bug" />
<!-- BEGIN open-source projects -->
packages/apps/Settings
diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java
index 8441c2acaa5..ecfda27237f 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettings.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettings.java
@@ -21,7 +21,6 @@ import android.accessibilityservice.AccessibilityShortcutInfo;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.ServiceInfo;
import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.Handler;
@@ -29,7 +28,6 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.Pair;
import android.view.InputDevice;
import android.view.accessibility.AccessibilityManager;
@@ -57,8 +55,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
/** Activity with the accessibility settings. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
@@ -458,20 +454,9 @@ public class AccessibilitySettings extends DashboardFragment implements
UserHandle.myUserId());
final List<AccessibilityActivityPreference> activityList =
preferenceHelper.createAccessibilityActivityPreferenceList(installedShortcutList);
- final Set<Pair<String, CharSequence>> packageLabelPairs =
- activityList.stream()
- .map(a11yActivityPref -> new Pair<>(
- a11yActivityPref.getPackageName(), a11yActivityPref.getLabel())
- ).collect(Collectors.toSet());
-
- // Remove duplicate item here, new a ArrayList to copy unmodifiable list result
- // (getInstalledAccessibilityServiceList).
+
final List<AccessibilityServiceInfo> installedServiceList = new ArrayList<>(
a11yManager.getInstalledAccessibilityServiceList());
- if (!packageLabelPairs.isEmpty()) {
- installedServiceList.removeIf(
- target -> containsPackageAndLabelInList(packageLabelPairs, target));
- }
final List<RestrictedPreference> serviceList =
preferenceHelper.createAccessibilityServicePreferenceList(installedServiceList);
@@ -482,16 +467,6 @@ public class AccessibilitySettings extends DashboardFragment implements
return preferenceList;
}
- private boolean containsPackageAndLabelInList(
- Set<Pair<String, CharSequence>> packageLabelPairs,
- AccessibilityServiceInfo targetServiceInfo) {
- final ServiceInfo serviceInfo = targetServiceInfo.getResolveInfo().serviceInfo;
- final String servicePackageName = serviceInfo.packageName;
- final CharSequence serviceLabel = serviceInfo.loadLabel(getPackageManager());
-
- return packageLabelPairs.contains(new Pair<>(servicePackageName, serviceLabel));
- }
-
private void initializePreBundledServicesMapFromArray(String categoryKey, int key) {
String[] services = getResources().getStringArray(key);
PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey);
diff --git a/src/com/android/settings/applications/AppInfoBase.java b/src/com/android/settings/applications/AppInfoBase.java
index 2c41be4ad41..1d774826c2d 100644
--- a/src/com/android/settings/applications/AppInfoBase.java
+++ b/src/com/android/settings/applications/AppInfoBase.java
@@ -18,6 +18,7 @@ package com.android.settings.applications;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import android.Manifest;
import android.app.Activity;
import android.app.Dialog;
import android.app.admin.DevicePolicyManager;
@@ -39,6 +40,7 @@ import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
@@ -135,8 +137,13 @@ public abstract class AppInfoBase extends SettingsPreferenceFragment
}
}
if (intent != null && intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
- mUserId = ((UserHandle) intent.getParcelableExtra(
- Intent.EXTRA_USER_HANDLE)).getIdentifier();
+ mUserId = ((UserHandle) intent.getParcelableExtra(Intent.EXTRA_USER_HANDLE))
+ .getIdentifier();
+ if (mUserId != UserHandle.myUserId() && !hasInteractAcrossUsersFullPermission()) {
+ Log.w(TAG, "Intent not valid.");
+ finish();
+ return "";
+ }
} else {
mUserId = UserHandle.myUserId();
}
@@ -163,6 +170,28 @@ public abstract class AppInfoBase extends SettingsPreferenceFragment
return mPackageName;
}
+ @VisibleForTesting
+ protected boolean hasInteractAcrossUsersFullPermission() {
+ Activity activity = getActivity();
+ if (!(activity instanceof SettingsActivity)) {
+ return false;
+ }
+ final String callingPackageName =
+ ((SettingsActivity) activity).getInitialCallingPackage();
+
+ if (TextUtils.isEmpty(callingPackageName)) {
+ Log.w(TAG, "Not able to get calling package name for permission check");
+ return false;
+ }
+ if (mPm.checkPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingPackageName)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "Package " + callingPackageName + " does not have required permission "
+ + Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ return false;
+ }
+ return true;
+ }
+
protected void setIntentAndFinish(boolean appChanged) {
Log.i(TAG, "appChanged=" + appChanged);
Intent intent = new Intent();
diff --git a/src/com/android/settings/users/AppRestrictionsFragment.java b/src/com/android/settings/users/AppRestrictionsFragment.java
index 1532448718c..c42e2f57b1d 100644
--- a/src/com/android/settings/users/AppRestrictionsFragment.java
+++ b/src/com/android/settings/users/AppRestrictionsFragment.java
@@ -651,7 +651,7 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen
int requestCode = generateCustomActivityRequestCode(
RestrictionsResultReceiver.this.preference);
AppRestrictionsFragment.this.startActivityForResult(
- restrictionsIntent, requestCode);
+ new Intent(restrictionsIntent), requestCode);
}
}
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
index 1463cd0b7f9..41206068d18 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
@@ -32,6 +32,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
@@ -50,6 +51,7 @@ import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.XmlTestUtils;
+import com.android.settings.testutils.shadow.ShadowAccessibilityManager;
import com.android.settings.testutils.shadow.ShadowApplicationPackageManager;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
@@ -75,7 +77,6 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowAccessibilityManager;
import org.robolectric.shadows.ShadowContentResolver;
import org.xmlpull.v1.XmlPullParserException;
@@ -87,6 +88,7 @@ import java.util.List;
/** Test for {@link AccessibilitySettings}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
+ ShadowAccessibilityManager.class,
ShadowBluetoothAdapter.class,
ShadowUserManager.class,
ShadowColorDisplayManager.class,
@@ -95,8 +97,10 @@ import java.util.List;
})
public class AccessibilitySettingsTest {
private static final String PACKAGE_NAME = "com.android.test";
- private static final String CLASS_NAME = PACKAGE_NAME + ".test_a11y_service";
- private static final ComponentName COMPONENT_NAME = new ComponentName(PACKAGE_NAME, CLASS_NAME);
+ private static final ComponentName SERVICE_COMPONENT_NAME =
+ new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".test_a11y_service");
+ private static final ComponentName ACTIVITY_COMPONENT_NAME =
+ new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".test_a11y_activity");
private static final String EMPTY_STRING = "";
private static final String DEFAULT_SUMMARY = "default summary";
private static final String DEFAULT_DESCRIPTION = "default description";
@@ -110,9 +114,7 @@ public class AccessibilitySettingsTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
@Spy
private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo(
- PACKAGE_NAME, CLASS_NAME);
- @Mock
- private AccessibilityShortcutInfo mShortcutInfo;
+ SERVICE_COMPONENT_NAME);
private ShadowAccessibilityManager mShadowAccessibilityManager;
@Mock
private LocalBluetoothManager mLocalBluetoothManager;
@@ -121,11 +123,11 @@ public class AccessibilitySettingsTest {
@Before
public void setup() {
- mShadowAccessibilityManager = Shadow.extract(AccessibilityManager.getInstance(mContext));
+ mShadowAccessibilityManager = Shadow.extract(
+ mContext.getSystemService(AccessibilityManager.class));
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(new ArrayList<>());
mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
- setMockAccessibilityShortcutInfo(mShortcutInfo);
Intent intent = new Intent();
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
@@ -368,7 +370,7 @@ public class AccessibilitySettingsTest {
mFragment.onContentChanged();
RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference(
- COMPONENT_NAME.flattenToString());
+ SERVICE_COMPONENT_NAME.flattenToString());
assertThat(preference).isNotNull();
@@ -388,7 +390,7 @@ public class AccessibilitySettingsTest {
mFragment.onResume();
RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference(
- COMPONENT_NAME.flattenToString());
+ SERVICE_COMPONENT_NAME.flattenToString());
assertThat(preference).isNotNull();
@@ -418,18 +420,44 @@ public class AccessibilitySettingsTest {
assertThat(pref).isNull();
}
- private AccessibilityServiceInfo getMockAccessibilityServiceInfo(String packageName,
- String className) {
- return getMockAccessibilityServiceInfo(new ComponentName(packageName, className));
+ @Test
+ public void testSameNamedServiceAndActivity_bothPreferencesExist() {
+ final PackageManager pm = mContext.getPackageManager();
+ AccessibilityServiceInfo a11yServiceInfo = mServiceInfo;
+ AccessibilityShortcutInfo a11yShortcutInfo = getMockAccessibilityShortcutInfo();
+ // Ensure the test service and activity have the same package name and label.
+ // Before this change, any service and activity with the same package name and
+ // label would cause the service to be hidden.
+ assertThat(a11yServiceInfo.getComponentName())
+ .isNotEqualTo(a11yShortcutInfo.getComponentName());
+ assertThat(a11yServiceInfo.getComponentName().getPackageName())
+ .isEqualTo(a11yShortcutInfo.getComponentName().getPackageName());
+ assertThat(a11yServiceInfo.getResolveInfo().serviceInfo.loadLabel(pm))
+ .isEqualTo(a11yShortcutInfo.getActivityInfo().loadLabel(pm));
+ // Prepare A11yManager with the test service and activity.
+ mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
+ List.of(mServiceInfo));
+ mShadowAccessibilityManager.setInstalledAccessibilityShortcutListAsUser(
+ List.of(getMockAccessibilityShortcutInfo()));
+ setupFragment();
+
+ // Both service and activity preferences should exist on the page.
+ RestrictedPreference servicePref = mFragment.getPreferenceScreen().findPreference(
+ a11yServiceInfo.getComponentName().flattenToString());
+ RestrictedPreference activityPref = mFragment.getPreferenceScreen().findPreference(
+ a11yShortcutInfo.getComponentName().flattenToString());
+ assertThat(servicePref).isNotNull();
+ assertThat(activityPref).isNotNull();
}
private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName componentName) {
- final ApplicationInfo applicationInfo = new ApplicationInfo();
- final ServiceInfo serviceInfo = new ServiceInfo();
+ final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
+ final ServiceInfo serviceInfo = Mockito.spy(new ServiceInfo());
applicationInfo.packageName = componentName.getPackageName();
serviceInfo.packageName = componentName.getPackageName();
serviceInfo.name = componentName.getClassName();
serviceInfo.applicationInfo = applicationInfo;
+ when(serviceInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL);
final ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.serviceInfo = serviceInfo;
@@ -445,14 +473,16 @@ public class AccessibilitySettingsTest {
return null;
}
- private void setMockAccessibilityShortcutInfo(AccessibilityShortcutInfo mockInfo) {
+ private AccessibilityShortcutInfo getMockAccessibilityShortcutInfo() {
+ AccessibilityShortcutInfo mockInfo = Mockito.mock(AccessibilityShortcutInfo.class);
final ActivityInfo activityInfo = Mockito.mock(ActivityInfo.class);
activityInfo.applicationInfo = new ApplicationInfo();
when(mockInfo.getActivityInfo()).thenReturn(activityInfo);
when(activityInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL);
when(mockInfo.loadSummary(any())).thenReturn(DEFAULT_SUMMARY);
when(mockInfo.loadDescription(any())).thenReturn(DEFAULT_DESCRIPTION);
- when(mockInfo.getComponentName()).thenReturn(COMPONENT_NAME);
+ when(mockInfo.getComponentName()).thenReturn(ACTIVITY_COMPONENT_NAME);
+ return mockInfo;
}
private void setInvisibleToggleFragmentType(AccessibilityServiceInfo info) {
diff --git a/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java b/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java
index 562212e3569..0ed56c0fec6 100644
--- a/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java
+++ b/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java
@@ -171,6 +171,32 @@ public class AppInfoWithHeaderTest {
assertThat(mAppInfoWithHeader.mAppEntry).isNotNull();
}
+ @Test
+ public void noCrossUserPermission_retrieveAppEntry_fail()
+ throws PackageManager.NameNotFoundException {
+ TestFragmentWithoutPermission testFragmentWithoutPermission =
+ new TestFragmentWithoutPermission();
+ final int userId = 1002;
+ final String packageName = "com.android.settings";
+
+ testFragmentWithoutPermission.mIntent.putExtra(Intent.EXTRA_USER_HANDLE,
+ new UserHandle(userId));
+ testFragmentWithoutPermission.mIntent.setData(Uri.fromParts("package",
+ packageName, null));
+ final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
+ entry.info = new ApplicationInfo();
+ entry.info.packageName = packageName;
+
+ when(testFragmentWithoutPermission.mState.getEntry(packageName, userId)).thenReturn(entry);
+ when(testFragmentWithoutPermission.mPm.getPackageInfoAsUser(eq(entry.info.packageName),
+ any(), eq(userId))).thenReturn(
+ testFragmentWithoutPermission.mPackageInfo);
+
+ testFragmentWithoutPermission.retrieveAppEntry();
+
+ assertThat(testFragmentWithoutPermission.mAppEntry).isNull();
+ }
+
public static class TestFragment extends AppInfoWithHeader {
PreferenceManager mManager;
@@ -223,6 +249,11 @@ public class AppInfoWithHeaderTest {
return mShadowContext;
}
+ @Override
+ protected boolean hasInteractAcrossUsersFullPermission() {
+ return true;
+ }
+
@Override
protected void onPackageRemoved() {
mPackageRemovedCalled = true;
@@ -233,4 +264,11 @@ public class AppInfoWithHeaderTest {
return mIntent;
}
}
+
+ private static final class TestFragmentWithoutPermission extends TestFragment {
+ @Override
+ protected boolean hasInteractAcrossUsersFullPermission() {
+ return false;
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java
index ce03a6def34..d597b7ea045 100644
--- a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java
@@ -39,23 +39,26 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.Manifest;
+import android.app.Application;
import android.app.IActivityManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.UserHandle;
-import androidx.fragment.app.testing.EmptyFragmentActivity;
import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import com.android.settings.SettingsActivity;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowActivityManager;
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
@@ -71,14 +74,14 @@ import org.robolectric.annotation.Config;
@Config(shadows = {ShadowActivityManager.class, ShadowFragment.class})
public class UserAspectRatioDetailsTest {
- @Rule
- public ActivityScenarioRule<EmptyFragmentActivity> rule =
- new ActivityScenarioRule<>(EmptyFragmentActivity.class);
-
@Mock
private UserAspectRatioManager mUserAspectRatioManager;
@Mock
private IActivityManager mAm;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private SettingsActivity mSettingsActivity;
private RadioWithImagePreference mRadioButtonPref;
private Context mContext;
@@ -93,6 +96,12 @@ public class UserAspectRatioDetailsTest {
mFragment = spy(new UserAspectRatioDetails());
when(mFragment.getContext()).thenReturn(mContext);
when(mFragment.getAspectRatioManager()).thenReturn(mUserAspectRatioManager);
+ when(mFragment.getActivity()).thenReturn(mSettingsActivity);
+ when(mSettingsActivity.getApplication()).thenReturn((Application) mContext);
+ when(mSettingsActivity.getInitialCallingPackage()).thenReturn("test.package");
+ when(mSettingsActivity.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.checkPermission(eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL),
+ any())).thenReturn(PackageManager.PERMISSION_GRANTED);
when(mUserAspectRatioManager.isOverrideToFullscreenEnabled(anyString(), anyInt()))
.thenReturn(false);
ShadowActivityManager.setService(mAm);
@@ -111,8 +120,10 @@ public class UserAspectRatioDetailsTest {
.getUserMinAspectRatioOrder(USER_MIN_ASPECT_RATIO_FULLSCREEN);
doReturn(2).when(mUserAspectRatioManager)
.getUserMinAspectRatioOrder(USER_MIN_ASPECT_RATIO_UNSET);
- rule.getScenario().onActivity(a -> doReturn(a).when(mFragment).getActivity());
final Bundle args = new Bundle();
+ Intent intent = new Intent();
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, new UserHandle(0));
+ args.putParcelable("intent", intent);
args.putString(ARG_PACKAGE_NAME, anyString());
mFragment.setArguments(args);
mFragment.onCreate(Bundle.EMPTY);
@@ -196,8 +207,10 @@ public class UserAspectRatioDetailsTest {
doReturn(true).when(mUserAspectRatioManager)
.hasAspectRatioOption(anyInt(), anyString());
- rule.getScenario().onActivity(a -> doReturn(a).when(mFragment).getActivity());
final Bundle args = new Bundle();
+ Intent intent = new Intent();
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, new UserHandle(0));
+ args.putParcelable("intent", intent);
args.putString(ARG_PACKAGE_NAME, anyString());
mFragment.setArguments(args);
mFragment.onCreate(Bundle.EMPTY);
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccessibilityManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccessibilityManager.java
index de7792c2ec0..fcd1e42c547 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccessibilityManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccessibilityManager.java
@@ -16,15 +16,18 @@
package com.android.settings.testutils.shadow;
+import android.accessibilityservice.AccessibilityShortcutInfo;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.ComponentName;
+import android.content.Context;
import android.util.ArrayMap;
import android.view.accessibility.AccessibilityManager;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import java.util.List;
import java.util.Map;
/**
@@ -33,9 +36,10 @@ import java.util.Map;
@Implements(AccessibilityManager.class)
public class ShadowAccessibilityManager extends org.robolectric.shadows.ShadowAccessibilityManager {
private Map<ComponentName, ComponentName> mA11yFeatureToTileMap = new ArrayMap<>();
+ private List<AccessibilityShortcutInfo> mInstalledAccessibilityShortcutList = List.of();
/**
- * Implements a hidden method {@link AccessibilityManager.getA11yFeatureToTileMap}
+ * Implements a hidden method {@link AccessibilityManager#getA11yFeatureToTileMap}
*/
@Implementation
public Map<ComponentName, ComponentName> getA11yFeatureToTileMap(@UserIdInt int userId) {
@@ -49,4 +53,22 @@ public class ShadowAccessibilityManager extends org.robolectric.shadows.ShadowAc
@NonNull Map<ComponentName, ComponentName> a11yFeatureToTileMap) {
mA11yFeatureToTileMap = a11yFeatureToTileMap;
}
+
+ /**
+ * Implements the hidden method
+ * {@link AccessibilityManager#getInstalledAccessibilityShortcutListAsUser}.
+ */
+ @Implementation
+ public List<AccessibilityShortcutInfo> getInstalledAccessibilityShortcutListAsUser(
+ @NonNull Context context, @UserIdInt int userId) {
+ return mInstalledAccessibilityShortcutList;
+ }
+
+ /**
+ * Sets the value to be returned by {@link #getInstalledAccessibilityShortcutListAsUser}.
+ */
+ public void setInstalledAccessibilityShortcutListAsUser(
+ @NonNull List<AccessibilityShortcutInfo> installedAccessibilityShortcutList) {
+ mInstalledAccessibilityShortcutList = installedAccessibilityShortcutList;
+ }
}
packages/modules/Bluetooth
diff --git a/system/bta/dm/bta_dm_disc.cc b/system/bta/dm/bta_dm_disc.cc
index b8e24551db..3e92f3e978 100644
--- a/system/bta/dm/bta_dm_disc.cc
+++ b/system/bta/dm/bta_dm_disc.cc
@@ -33,9 +33,11 @@
#include "bta/dm/bta_dm_disc_legacy.h"
#include "bta/include/bta_gatt_api.h"
#include "com_android_bluetooth_flags.h"
+#include "btif/include/btif_storage.h"
#include "common/circular_buffer.h"
#include "common/init_flags.h"
#include "common/strings.h"
+#include "device/include/interop.h"
#include "internal_include/bt_target.h"
#include "main/shim/dumpsys.h"
#include "os/logging/log_adapter.h"
@@ -333,8 +335,17 @@ static void bta_dm_disc_result(tBTA_DM_SVC_RES& disc_result) {
bta_dm_discovery_cb.service_search_cbacks.on_service_discovery_results(
r.bd_addr, r.uuids, r.result);
} else {
+ char remote_name[BD_NAME_LEN] = "";
bta_dm_discovery_cb.transports &= ~BT_TRANSPORT_LE;
- GAP_BleReadPeerPrefConnParams(bta_dm_discovery_cb.peer_bdaddr);
+ if (btif_storage_get_stored_remote_name(bta_dm_discovery_cb.peer_bdaddr, remote_name) &&
+ interop_match_name(INTEROP_DISABLE_LE_CONN_PREFERRED_PARAMS, remote_name)) {
+ // Some devices provide PPCP values that are incompatible with the device-side firmware.
+ log::info("disable PPCP read: interop matched name {} address {}", remote_name,
+ bta_dm_discovery_cb.peer_bdaddr);
+ } else {
+ log::info("reading PPCP");
+ GAP_BleReadPeerPrefConnParams(bta_dm_discovery_cb.peer_bdaddr);
+ }
bta_dm_discovery_cb.service_search_cbacks.on_gatt_results(
bta_dm_discovery_cb.peer_bdaddr, BD_NAME{}, disc_result.gatt_uuids,
diff --git a/system/conf/interop_database.conf b/system/conf/interop_database.conf
index a275f6bc6b..8f190ab9ee 100644
--- a/system/conf/interop_database.conf
+++ b/system/conf/interop_database.conf
@@ -194,6 +194,9 @@ Motorola Keyboard KZ500 v122 = Name_Based
[INTEROP_DISABLE_LE_CONN_PREFERRED_PARAMS]
BSMBB09DS = Name_Based
ELECOM = Name_Based
+Dexcom = Name_Based
+DXCM = Name_Based
+DX0 = Name_Based
# Disable role switch for headsets/car-kits
# Some car kits allow role switch but when DUT initiates role switch
diff --git a/system/device/test/interop_test.cc b/system/device/test/interop_test.cc
index 3ad3db2ce9..24c2c2ab37 100644
--- a/system/device/test/interop_test.cc
+++ b/system/device/test/interop_test.cc
@@ -433,6 +433,9 @@ TEST_F(InteropTest, test_name_hit) {
"Motorola Keyboard KZ500"));
EXPECT_TRUE(interop_match_name(INTEROP_DISABLE_LE_CONN_PREFERRED_PARAMS,
"BSMBB09DS"));
+ EXPECT_TRUE(interop_match_name(INTEROP_DISABLE_LE_CONN_PREFERRED_PARAMS, "DXCMog"));
+ EXPECT_TRUE(interop_match_name(INTEROP_DISABLE_LE_CONN_PREFERRED_PARAMS, "Dexcom 123"));
+ EXPECT_TRUE(interop_match_name(INTEROP_DISABLE_LE_CONN_PREFERRED_PARAMS, "DX01ab"));
EXPECT_TRUE(interop_match_name(INTEROP_DISABLE_AAC_CODEC, "abramtek M1"));
EXPECT_TRUE(
interop_match_name(INTEROP_DISABLE_AAC_VBR_CODEC, "Audi_MMI_2781"));
packages/modules/Wifi
diff --git a/service/java/com/android/server/wifi/WifiConfigurationUtil.java b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
index 4421fcd08e..501a071b34 100644
--- a/service/java/com/android/server/wifi/WifiConfigurationUtil.java
+++ b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
@@ -16,7 +16,11 @@
package com.android.server.wifi;
+import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_NUM;
import static android.net.wifi.WifiManager.ALL_ZEROS_MAC_ADDRESS;
+import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_NUMBER_OF_OI;
+import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_OI_VALUE;
+import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_URL_BYTES;
import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes;
@@ -31,6 +35,7 @@ import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkSpecifier;
import android.net.wifi.WifiScanner;
import android.net.wifi.WifiSsid;
+import android.net.wifi.hotspot2.PasspointConfiguration;
import android.os.PatternMatcher;
import android.text.TextUtils;
import android.util.Log;
@@ -46,8 +51,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* WifiConfiguration utility for any {@link android.net.wifi.WifiConfiguration} related operations.
@@ -70,6 +77,7 @@ public class WifiConfigurationUtil {
private static final int PSK_SAE_HEX_LEN = 64;
private static final int WEP104_KEY_BYTES_LEN = 13;
private static final int WEP40_KEY_BYTES_LEN = 5;
+ private static final int MAX_STRING_LENGTH = 512;
@VisibleForTesting
public static final String PASSWORD_MASK = "*";
@@ -750,7 +758,8 @@ public class WifiConfigurationUtil {
if (!validateSsid(config.SSID, isAdd)) {
return false;
}
- if (!validateBssid(config.BSSID)) {
+ if (!validateBssid(config.BSSID) || !validateBssid(config.dhcpServer)
+ || !validateBssid(config.defaultGwMacAddress)) {
return false;
}
if (!validateBitSets(config)) {
@@ -759,9 +768,22 @@ public class WifiConfigurationUtil {
if (!validateKeyMgmt(config.allowedKeyManagement)) {
return false;
}
- if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WEP)
- && config.wepKeys != null
- && !validateWepKeys(config.wepKeys, config.wepTxKeyIndex, isAdd)) {
+ if (!validateSecurityParameters(config.getSecurityParamsList())) {
+ return false;
+ }
+ if (!validatePasspoint(config)) {
+ return false;
+ }
+ if (!validateNetworkSelectionStatus(config.getNetworkSelectionStatus())) {
+ return false;
+ }
+
+ if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WEP)) {
+ if (config.wepKeys != null
+ && !validateWepKeys(config.wepKeys, config.wepTxKeyIndex, isAdd)) {
+ return false;
+ }
+ } else if (!validateWepKeys(config.wepKeys, config.wepTxKeyIndex, false)) {
return false;
}
if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)
@@ -793,10 +815,92 @@ public class WifiConfigurationUtil {
if (!validateIpConfiguration(config.getIpConfiguration())) {
return false;
}
+
+ if (config.getDppConnector().length > MAX_URL_BYTES
+ || config.getDppCSignKey().length > MAX_URL_BYTES
+ || config.getDppPrivateEcKey().length > MAX_URL_BYTES
+ || config.getDppNetAccessKey().length > MAX_URL_BYTES) {
+ return false;
+ }
// TBD: Validate some enterprise params as well in the future here.
return true;
}
+ private static boolean validateStringField(String field, int maxLength) {
+ return field == null || field.length() <= maxLength;
+ }
+
+ private static boolean validatePasspoint(WifiConfiguration config) {
+ if (!validateStringField(config.FQDN, PasspointConfiguration.MAX_STRING_LENGTH)) {
+ return false;
+ }
+ if (!validateStringField(config.providerFriendlyName,
+ PasspointConfiguration.MAX_STRING_LENGTH)) {
+ return false;
+ }
+ if (!validateRoamingConsortiumIds(config.roamingConsortiumIds)) {
+ return false;
+ }
+ if (!validateUpdateIdentifier(config.updateIdentifier)) {
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean validateUpdateIdentifier(String updateIdentifier) {
+ if (TextUtils.isEmpty(updateIdentifier)) {
+ return true;
+ }
+ try {
+ Integer.valueOf(updateIdentifier);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean validateNetworkSelectionStatus(
+ WifiConfiguration.NetworkSelectionStatus status) {
+ if (status == null) {
+ return false;
+ }
+ return validateStringField(status.getConnectChoice(), MAX_STRING_LENGTH)
+ && validateBssid(status.getNetworkSelectionBSSID());
+ }
+
+ private static boolean validateRoamingConsortiumIds(long[] roamingConsortiumIds) {
+ if (roamingConsortiumIds != null) {
+ if (roamingConsortiumIds.length > MAX_NUMBER_OF_OI) {
+ Log.d(TAG, "too many Roaming Consortium Organization Identifiers in the "
+ + "profile");
+ return false;
+ }
+ for (long oi : roamingConsortiumIds) {
+ if (oi < 0 || oi > MAX_OI_VALUE) {
+ Log.d(TAG, "Organization Identifiers is out of range");
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private static boolean validateSecurityParameters(List<SecurityParams> paramsList) {
+ Set<Integer> uniqueSecurityTypes = new HashSet<>(SECURITY_TYPE_NUM + 1);
+ for (SecurityParams params : paramsList) {
+ int securityType = params.getSecurityType();
+ if (securityType < 0 || securityType > SECURITY_TYPE_NUM) {
+ return false;
+ }
+ if (uniqueSecurityTypes.contains(securityType)) {
+ return false;
+ }
+ uniqueSecurityTypes.add(securityType);
+ }
+ return true;
+
+ }
+
private static boolean validateBssidPattern(
Pair<MacAddress, MacAddress> bssidPatternMatcher) {
if (bssidPatternMatcher == null) return true;
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 30066a2387..22fc6ee42f 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -3891,6 +3891,11 @@ public class WifiServiceImpl extends BaseWifiService {
boolean isCamera = mWifiPermissionsUtil.checkCameraPermission(callingUid);
boolean isSystem = mWifiPermissionsUtil.isSystem(packageName, callingUid);
boolean isPrivileged = isPrivileged(callingPid, callingUid);
+ if (!isPrivileged && !isSystem && !isAdmin && config.getBssidAllowlistInternal() != null) {
+ mLog.info("addOrUpdateNetwork with allow bssid list is not allowed for uid=%")
+ .c(callingUid).flush();
+ return -1;
+ }
if (!isTargetSdkLessThanQOrPrivileged(packageName, callingPid, callingUid)) {
mLog.info("addOrUpdateNetwork not allowed for uid=%").c(callingUid).flush();
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
index 8dbe628f5c..81b182c9df 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
@@ -18,6 +18,7 @@ package com.android.server.wifi;
import static android.net.wifi.WifiEnterpriseConfig.OCSP_NONE;
import static android.net.wifi.WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS;
+import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_URL_BYTES;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -53,6 +54,7 @@ import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
+import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
@@ -77,6 +79,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest {
new UserInfo(CURRENT_USER_ID, "owner", 0),
new UserInfo(CURRENT_USER_MANAGED_PROFILE_USER_ID, "managed profile", 0));
private static final long SUPPORTED_FEATURES_ALL = Long.MAX_VALUE;
+ private final String mGeneratedString256 = "a".repeat(256);
private MockitoSession mSession;
@@ -1606,4 +1609,76 @@ public class WifiConfigurationUtilTest extends WifiBaseTest {
assertTrue(WifiConfigurationUtil.isConfigLinkable(saeConfig));
assertFalse(WifiConfigurationUtil.isConfigLinkable(saeDisabledConfig));
}
+
+ @Test
+ public void testWepKeyOnNonWepConfig() {
+ WifiConfiguration pskConfig = WifiConfigurationTestUtil.createPskNetwork();
+ pskConfig.wepKeys = new String[4];
+ pskConfig.wepKeys[0] = mGeneratedString256;
+ assertFalse(WifiConfigurationUtil.validate(pskConfig, SUPPORTED_FEATURES_ALL,
+ WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ @Test
+ public void testInvalidFqdnAndFriendlyName() {
+ WifiConfiguration pskConfig = WifiConfigurationTestUtil.createPskNetwork();
+
+ pskConfig.FQDN = mGeneratedString256;
+ assertFalse(WifiConfigurationUtil.validate(pskConfig, SUPPORTED_FEATURES_ALL,
+ WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ pskConfig.FQDN = null;
+ pskConfig.providerFriendlyName = mGeneratedString256;
+ assertFalse(WifiConfigurationUtil.validate(pskConfig, SUPPORTED_FEATURES_ALL,
+ WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ @Test
+ public void testInvalidDhcpAndGtw() {
+ WifiConfiguration pskConfig = WifiConfigurationTestUtil.createPskNetwork();
+ pskConfig.dhcpServer = TEST_BSSID;
+ pskConfig.defaultGwMacAddress = TEST_BSSID;
+ assertTrue(WifiConfigurationUtil.validate(pskConfig, SUPPORTED_FEATURES_ALL,
+ WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ pskConfig.dhcpServer = mGeneratedString256;
+ assertFalse(WifiConfigurationUtil.validate(pskConfig, SUPPORTED_FEATURES_ALL,
+ WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ pskConfig.dhcpServer = TEST_BSSID;
+ pskConfig.defaultGwMacAddress = mGeneratedString256;
+ assertFalse(WifiConfigurationUtil.validate(pskConfig, SUPPORTED_FEATURES_ALL,
+ WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ @Test
+ public void testInvalidSecurityParameter() {
+ WifiConfiguration pskConfig = WifiConfigurationTestUtil.createPskNetwork();
+ List<SecurityParams> securityParamsList = new ArrayList<>();
+ securityParamsList.add(SecurityParams.createSecurityParamsBySecurityType(
+ WifiConfiguration.SECURITY_TYPE_PSK));
+ securityParamsList.add(SecurityParams.createSecurityParamsBySecurityType(
+ WifiConfiguration.SECURITY_TYPE_PSK));
+
+ pskConfig.setSecurityParams(securityParamsList);
+ assertFalse(WifiConfigurationUtil.validate(pskConfig, SUPPORTED_FEATURES_ALL,
+ WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ @Test
+ public void testInvalidUserConnectChoice() {
+ WifiConfiguration pskConfig = WifiConfigurationTestUtil.createPskNetwork();
+ String generatedString513 = "a".repeat(513);
+ pskConfig.getNetworkSelectionStatus().setConnectChoice(generatedString513);
+
+ assertFalse(WifiConfigurationUtil.validate(pskConfig, SUPPORTED_FEATURES_ALL,
+ WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ @Test
+ public void testInvalidDppConfig() {
+ WifiConfiguration pskConfig = WifiConfigurationTestUtil.createPskNetwork();
+ String generatedString = "a".repeat(MAX_URL_BYTES + 1);
+ pskConfig.setDppConfigurator(generatedString.getBytes(StandardCharsets.UTF_8));
+ assertFalse(WifiConfigurationUtil.validate(pskConfig, SUPPORTED_FEATURES_ALL,
+ WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
}
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index 1e25a458c0..90dc2c86ea 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -6845,6 +6845,31 @@ public class WifiServiceImplTest extends WifiBaseTest {
verify(mWifiMetrics).incrementNumAddOrUpdateNetworkCalls();
}
+ /**
+ * Verify that add or update networks is allowed for apps targeting below Q SDK.
+ */
+ @Test
+ public void testAddOrUpdateNetworkWithBssidAllowListIsNotAllowedForAppsNotPrivileged()
+ throws Exception {
+ doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
+ .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
+ when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt(), any(), eq(false))).thenReturn(
+ new NetworkUpdateResult(0));
+ when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
+ eq(Build.VERSION_CODES.Q), anyInt())).thenReturn(true);
+
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ config.setBssidAllowlist(Collections.emptyList());
+ mLooper.startAutoDispatch();
+ assertEquals(-1,
+ mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME, mAttribution));
+ mLooper.stopAutoDispatchAndIgnoreExceptions();
+
+ verifyCheckChangePermission(TEST_PACKAGE_NAME);
+ verify(mWifiConfigManager, never()).addOrUpdateNetwork(any(), anyInt(), any(), eq(false));
+ verify(mWifiMetrics, never()).incrementNumAddOrUpdateNetworkCalls();
+ }
+
/**
* Verify that add or update networks is not allowed for apps targeting below Q SDK
* when DISALLOW_ADD_WIFI_CONFIG user restriction is set.
packages/providers/MediaProvider
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 72f45d6a1..34e726ab0 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -8219,6 +8219,8 @@ public class MediaProvider extends ContentProvider {
case IMAGES_MEDIA_ID:
case DOWNLOADS_ID:
case FILES_ID:
+ // Check if the caller has the required permissions to do placement
+ enforceCallingPermission(uri, extras, true);
break;
default:
throw new IllegalArgumentException("Movement of " + uri
system/core
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 6d422c6141..1ec8634444 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -335,6 +335,9 @@ class SnapshotManager final : public ISnapshotManager {
// after loading selinux policy.
bool PrepareSnapuserdArgsForSelinux(std::vector<std::string>* snapuserd_argv);
+ // If snapuserd from first stage init was started from system partition.
+ bool MarkSnapuserdFromSystem();
+
// Detach dm-user devices from the first stage snapuserd. Load
// new dm-user tables after loading selinux policy.
bool DetachFirstStageSnapuserdForSelinux();
@@ -670,6 +673,7 @@ class SnapshotManager final : public ISnapshotManager {
std::string GetForwardMergeIndicatorPath();
std::string GetOldPartitionMetadataPath();
std::string GetBootSnapshotsWithoutSlotSwitchPath();
+ std::string GetSnapuserdFromSystemPath();
const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index c01360e0ba..e4a6153a84 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -20,6 +20,7 @@
#include <sys/file.h>
#include <sys/types.h>
#include <sys/unistd.h>
+#include <sys/xattr.h>
#include <filesystem>
#include <optional>
@@ -88,7 +89,10 @@ static constexpr char kBootSnapshotsWithoutSlotSwitch[] =
"/metadata/ota/snapshot-boot-without-slot-switch";
static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
+static constexpr char kSnapuserdFromSystem[] = "/metadata/ota/snapuserd-from-system";
static constexpr auto kUpdateStateCheckInterval = 2s;
+static constexpr char kOtaFileContext[] = "u:object_r:ota_metadata_file:s0";
+
/*
* The readahead size is set to 32kb so that
* there is no significant memory pressure (/proc/pressure/memory) during boot.
@@ -318,7 +322,7 @@ bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock, const std::function
std::vector<std::string> files = {
GetSnapshotBootIndicatorPath(), GetRollbackIndicatorPath(),
GetForwardMergeIndicatorPath(), GetOldPartitionMetadataPath(),
- GetBootSnapshotsWithoutSlotSwitchPath(),
+ GetBootSnapshotsWithoutSlotSwitchPath(), GetSnapuserdFromSystemPath(),
};
for (const auto& file : files) {
RemoveFileIfExists(file);
@@ -1457,6 +1461,10 @@ std::string SnapshotManager::GetRollbackIndicatorPath() {
return metadata_dir_ + "/" + android::base::Basename(kRollbackIndicatorPath);
}
+std::string SnapshotManager::GetSnapuserdFromSystemPath() {
+ return metadata_dir_ + "/" + android::base::Basename(kSnapuserdFromSystem);
+}
+
std::string SnapshotManager::GetForwardMergeIndicatorPath() {
return metadata_dir_ + "/allow-forward-merge";
}
@@ -2122,6 +2130,34 @@ bool SnapshotManager::UpdateUsesODirect(LockedFile* lock) {
return update_status.o_direct();
}
+bool SnapshotManager::MarkSnapuserdFromSystem() {
+ auto path = GetSnapuserdFromSystemPath();
+
+ if (!android::base::WriteStringToFile("1", path)) {
+ PLOG(ERROR) << "Unable to write to vendor update path: " << path;
+ return false;
+ }
+
+ unique_fd fd(open(path.c_str(), O_PATH));
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to open file: " << path;
+ return false;
+ }
+
+ /*
+ * This function is invoked by first stage init and hence we need to
+ * explicitly set the correct selinux label for this file as update_engine
+ * will try to remove this file later on once the snapshot merge is
+ * complete.
+ */
+ if (fsetxattr(fd.get(), XATTR_NAME_SELINUX, kOtaFileContext, strlen(kOtaFileContext) + 1, 0) <
+ 0) {
+ PLOG(ERROR) << "fsetxattr for the path: " << path << " failed";
+ }
+
+ return true;
+}
+
/*
* Please see b/304829384 for more details.
*
@@ -2158,14 +2194,35 @@ bool SnapshotManager::UpdateUsesODirect(LockedFile* lock) {
* iii: If both (i) and (ii) are true, then use the dm-snapshot based
* approach.
*
+ * 3: Post OTA reboot, if the vendor partition was updated from Android 12 to
+ * any other release post Android 12, then snapuserd binary will be "system"
+ * partition as post Android 12, init_boot will contain a copy of snapuserd
+ * binary. Thus, during first stage init, if init is able to communicate to
+ * daemon, that gives us a signal that the binary is from "system" copy. Hence,
+ * there is no need to fallback to legacy dm-snapshot. Thus, init will use a
+ * marker in /metadata to signal that the snapuserd binary from first stage init
+ * can handle userspace snapshots.
+ *
*/
bool SnapshotManager::IsLegacySnapuserdPostReboot() {
- if (is_legacy_snapuserd_.has_value() && is_legacy_snapuserd_.value() == true) {
- auto slot = GetCurrentSlot();
- if (slot == Slot::Target) {
+ auto slot = GetCurrentSlot();
+ if (slot == Slot::Target) {
+ /*
+ If this marker is present, the daemon can handle userspace snapshots.
+ During post-OTA reboot, this implies that the vendor partition is
+ Android 13 or higher. If the snapshots were created on an
+ Android 12 vendor, this means the vendor partition has been updated.
+ */
+ if (access(GetSnapuserdFromSystemPath().c_str(), F_OK) == 0) {
+ is_snapshot_userspace_ = true;
+ return false;
+ }
+ // If the marker isn't present and if the vendor is still in Android 12
+ if (is_legacy_snapuserd_.has_value() && is_legacy_snapuserd_.value() == true) {
return true;
}
}
+
return false;
}
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 5d3a273548..55cce6eaab 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -395,12 +395,7 @@ bool FirstStageMountVBootV2::CreateSnapshotPartitions(SnapshotManager* sm) {
use_snapuserd_ = sm->IsSnapuserdRequired();
if (use_snapuserd_) {
- if (sm->UpdateUsesUserSnapshots()) {
- LaunchFirstStageSnapuserd();
- } else {
- LOG(FATAL) << "legacy virtual-ab is no longer supported";
- return false;
- }
+ LaunchFirstStageSnapuserd();
}
sm->SetUeventRegenCallback([this](const std::string& device) -> bool {
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index 9e3ff4175e..2370bc2050 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -100,6 +100,10 @@ void LaunchFirstStageSnapuserd() {
}
if (client->SupportsSecondStageSocketHandoff()) {
setenv(kSnapuserdFirstStageInfoVar, "socket", 1);
+ auto sm = SnapshotManager::NewForFirstStageMount();
+ if (!sm->MarkSnapuserdFromSystem()) {
+ LOG(ERROR) << "Failed to update MarkSnapuserdFromSystem";
+ }
}
setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);