Merge "Add WearApp with scaffolding for navigation." into main
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index f40efc8..218d4bb 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -186,6 +186,8 @@
/**
* Get the binder transaction observer for this process.
*
+ * TODO(b/299356196): only applies to Java code, not C++/Rust
+ *
* @hide
*/
public static void setObserver(@Nullable BinderInternal.Observer observer) {
@@ -202,6 +204,8 @@
* that require a result must be sent as {@link IBinder#FLAG_ONEWAY} calls
* which deliver results through a callback interface.
*
+ * TODO(b/299355525): only applies to Java code, not C++/Rust
+ *
* @hide
*/
public static void setWarnOnBlocking(boolean warnOnBlocking) {
@@ -218,6 +222,8 @@
* interfaces hosted by package that could be upgraded or replaced,
* otherwise you risk system instability if that remote interface wedges.
*
+ * TODO(b/299355525): only applies to Java code, not C++/Rust
+ *
* @hide
*/
public static IBinder allowBlocking(IBinder binder) {
@@ -1307,6 +1313,8 @@
int callingUid) {
// Make sure the observer won't change while processing a transaction.
final BinderInternal.Observer observer = sObserver;
+
+ // TODO(b/299356196): observer should also observe transactions in native code
final CallSession callSession =
observer != null ? observer.callStarted(this, code, UNSET_WORKSOURCE) : null;
// Theoretically, we should call transact, which will call onTransact,
@@ -1329,7 +1337,7 @@
final boolean tracingEnabled = tagEnabled && transactionTraceName != null;
try {
- // TODO - this logic should not be in Java - it should be in native
+ // TODO(b/299356201) - this logic should not be in Java - it should be in native
// code in libbinder so that it works for all binder users.
final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;
if (heavyHitterWatcher != null && callingUid != -1) {
@@ -1340,9 +1348,9 @@
Trace.traceBegin(Trace.TRACE_TAG_AIDL, transactionTraceName);
}
- // TODO - this logic should not be in Java - it should be in native
- // code in libbinder so that it works for all binder users. Further,
- // this should not re-use flags.
+ // TODO(b/299353919) - this logic should not be in Java - it should be
+ // in native code in libbinder so that it works for all binder users.
+ // Further, this should not re-use flags.
if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0 && callingUid != -1) {
AppOpsManager.startNotedAppOpsCollection(callingUid);
try {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 05063365..ac0e493 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -27,6 +27,7 @@
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+
import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
import android.R;
@@ -240,7 +241,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastMath;
import com.android.internal.util.Preconditions;
-import com.android.text.flags.Flags;
import libcore.util.EmptyArray;
@@ -1634,7 +1634,7 @@
}
if (CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH)) {
- mUseBoundsForWidth = Flags.useBoundsForWidth();
+ mUseBoundsForWidth = false; // TODO: Connect to the flag.
} else {
mUseBoundsForWidth = false;
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index a5ee19e..cdfc4c8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -879,14 +879,12 @@
// Skip resolving if the activity is on a pinned TaskFragmentContainer.
// TODO(b/243518738): skip resolving for overlay container.
- if (container != null) {
- final TaskContainer taskContainer = container.getTaskContainer();
- if (taskContainer.isTaskFragmentContainerPinned(container)) {
- return true;
- }
+ final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null;
+ if (container != null && taskContainer != null
+ && taskContainer.isTaskFragmentContainerPinned(container)) {
+ return true;
}
- final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null;
if (!isOnReparent && taskContainer != null
&& taskContainer.getTopNonFinishingTaskFragmentContainer(false /* includePin */)
!= container) {
@@ -895,6 +893,28 @@
return true;
}
+ // Ensure the top TaskFragments are updated to the right config if activity is resolved
+ // to a new TaskFragment while pin TF exists.
+ final boolean handled = resolveActivityToContainerByRule(wct, activity, container,
+ isOnReparent);
+ if (handled && taskContainer != null) {
+ final SplitPinContainer splitPinContainer = taskContainer.getSplitPinContainer();
+ if (splitPinContainer != null) {
+ final TaskFragmentContainer resolvedContainer = getContainerWithActivity(activity);
+ if (resolvedContainer != null && resolvedContainer.getRunningActivityCount() <= 1) {
+ updateContainer(wct, splitPinContainer.getSecondaryContainer());
+ }
+ }
+ }
+ return handled;
+ }
+
+ /**
+ * Resolves the activity to a {@link TaskFragmentContainer} according to the Split-rules.
+ */
+ boolean resolveActivityToContainerByRule(@NonNull WindowContainerTransaction wct,
+ @NonNull Activity activity, @Nullable TaskFragmentContainer container,
+ boolean isOnReparent) {
/*
* We will check the following to see if there is any embedding rule matched:
* 1. Whether the new launched activity should always expand.
@@ -1301,6 +1321,26 @@
}
}
+ // Ensure the top TaskFragments are updated to the right config if the intent is resolved
+ // to a new TaskFragment while pin TF exists.
+ final TaskFragmentContainer launchingContainer = resolveStartActivityIntentByRule(wct,
+ taskId, intent, launchingActivity);
+ if (launchingContainer != null && launchingContainer.getRunningActivityCount() == 0) {
+ final SplitPinContainer splitPinContainer =
+ launchingContainer.getTaskContainer().getSplitPinContainer();
+ if (splitPinContainer != null) {
+ updateContainer(wct, splitPinContainer.getSecondaryContainer());
+ }
+ }
+ return launchingContainer;
+ }
+
+ /**
+ * Resolves the intent to a {@link TaskFragmentContainer} according to the Split-rules.
+ */
+ @Nullable
+ TaskFragmentContainer resolveStartActivityIntentByRule(@NonNull WindowContainerTransaction wct,
+ int taskId, @NonNull Intent intent, @Nullable Activity launchingActivity) {
/*
* We will check the following to see if there is any embedding rule matched:
* 1. Whether the new activity intent should always expand.
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index fe1034a..3f609a2 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -65,12 +65,6 @@
* [NoteTaskController], ensure custom actions can be triggered (i.e., keyboard shortcut).
*/
private fun initializeHandleSystemKey() {
- val callbacks =
- object : CommandQueue.Callbacks {
- override fun handleSystemKey(key: KeyEvent) {
- key.toNoteTaskEntryPointOrNull()?.let(controller::showNoteTask)
- }
- }
commandQueue.addCallback(callbacks)
}
@@ -142,7 +136,7 @@
*/
private fun KeyEvent.toNoteTaskEntryPointOrNull(): NoteTaskEntryPoint? =
when {
- keyCode == KEYCODE_STYLUS_BUTTON_TAIL -> TAIL_BUTTON
+ keyCode == KEYCODE_STYLUS_BUTTON_TAIL && action == KeyEvent.ACTION_UP -> TAIL_BUTTON
keyCode == KEYCODE_N && isMetaPressed && isCtrlPressed -> KEYBOARD_SHORTCUT
else -> null
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index 95bb3e0..162b7b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -22,6 +22,8 @@
import android.testing.AndroidTestingRunner
import android.view.KeyEvent
import android.view.KeyEvent.ACTION_DOWN
+import android.view.KeyEvent.ACTION_UP
+import android.view.KeyEvent.KEYCODE_N
import android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
@@ -30,7 +32,6 @@
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -43,7 +44,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -66,6 +66,7 @@
private val executor = FakeExecutor(FakeSystemClock())
private val userTracker = FakeUserTracker()
+ private val handlerCallbacks = mutableListOf<Runnable>()
@Before
fun setUp() {
@@ -74,19 +75,27 @@
}
private fun createUnderTest(
- isEnabled: Boolean,
- bubbles: Bubbles?,
+ isEnabled: Boolean,
+ bubbles: Bubbles?,
): NoteTaskInitializer =
- NoteTaskInitializer(
- controller = controller,
- commandQueue = commandQueue,
- optionalBubbles = Optional.ofNullable(bubbles),
- isEnabled = isEnabled,
- roleManager = roleManager,
- userTracker = userTracker,
- keyguardUpdateMonitor = keyguardMonitor,
- backgroundExecutor = executor,
- )
+ NoteTaskInitializer(
+ controller = controller,
+ commandQueue = commandQueue,
+ optionalBubbles = Optional.ofNullable(bubbles),
+ isEnabled = isEnabled,
+ roleManager = roleManager,
+ userTracker = userTracker,
+ keyguardUpdateMonitor = keyguardMonitor,
+ backgroundExecutor = executor,
+ )
+
+ private fun createKeyEvent(
+ action: Int,
+ code: Int,
+ downTime: Long = 0L,
+ eventTime: Long = 0L,
+ metaState: Int = 0
+ ): KeyEvent = KeyEvent(downTime, eventTime, action, code, 0 /*repeat*/, metaState)
@Test
fun initialize_withUserUnlocked() {
@@ -120,12 +129,12 @@
underTest.initialize()
verifyZeroInteractions(
- commandQueue,
- bubbles,
- controller,
- roleManager,
- userManager,
- keyguardMonitor,
+ commandQueue,
+ bubbles,
+ controller,
+ roleManager,
+ userManager,
+ keyguardMonitor,
)
}
@@ -136,18 +145,23 @@
underTest.initialize()
verifyZeroInteractions(
- commandQueue,
- bubbles,
- controller,
- roleManager,
- userManager,
- keyguardMonitor,
+ commandQueue,
+ bubbles,
+ controller,
+ roleManager,
+ userManager,
+ keyguardMonitor,
)
}
@Test
fun initialize_handleSystemKey() {
- val expectedKeyEvent = KeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL)
+ val expectedKeyEvent =
+ createKeyEvent(
+ ACTION_DOWN,
+ KEYCODE_N,
+ metaState = KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
underTest.initialize()
val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
@@ -176,7 +190,7 @@
underTest.initialize()
val callback = withArgCaptor {
verify(roleManager)
- .addOnRoleHoldersChangedListenerAsUser(any(), capture(), eq(UserHandle.ALL))
+ .addOnRoleHoldersChangedListenerAsUser(any(), capture(), eq(UserHandle.ALL))
}
callback.onRoleHoldersChanged(ROLE_NOTES, userTracker.userHandle)
@@ -203,4 +217,22 @@
verify(controller, times(2)).updateNoteTaskForCurrentUserAndManagedProfiles()
}
+
+ @Test
+ fun tailButtonGestureDetection_singlePress_shouldShowNoteTaskOnUp() {
+ val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+ underTest.initialize()
+ val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
+ )
+ verify(controller, never()).showNoteTask(any())
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 50)
+ )
+
+ verify(controller).showNoteTask(any())
+ }
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 0cfdaf2..10b6052 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -195,8 +195,12 @@
mApplicationKeyStorage = applicationKeyStorage;
mTestCertHelper = testOnlyInsecureCertificateHelper;
mCleanupManager = cleanupManager;
- // Clears data for removed users.
- mCleanupManager.verifyKnownUsers();
+ try {
+ // Clears data for removed users.
+ mCleanupManager.verifyKnownUsers();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to verify known users", e);
+ }
try {
mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
} catch (NoSuchAlgorithmException e) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4d38239..b420acd 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4773,7 +4773,7 @@
case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
- if (down && mStylusButtonsEnabled) {
+ if (mStylusButtonsEnabled) {
sendSystemKeyToStatusBarAsync(event);
}
result &= ~ACTION_PASS_TO_USER;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9c9c63f..f38f6b0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1843,8 +1843,12 @@
RemoteCallback navigationObserver, BackAnimationAdapter adapter) {
mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
"startBackNavigation()");
-
- return mBackNavigationController.startBackNavigation(navigationObserver, adapter);
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ return mBackNavigationController.startBackNavigation(navigationObserver, adapter);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e523119..9bfc553 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -795,17 +795,13 @@
return false;
}
- // Try pausing the existing resumed activity in the same TaskFragment if any.
- final TaskFragment taskFragment = r.getTaskFragment();
- if (taskFragment != null && taskFragment.getResumedActivity() != null) {
- if (taskFragment.startPausing(mUserLeaving, false /* uiSleeping */, r, "realStart")) {
- return false;
- }
+ // Try pausing the existing resumed activity in the Task if any.
+ final Task task = r.getTask();
+ if (task.pauseActivityIfNeeded(r, "realStart")) {
+ return false;
}
- final Task task = r.getTask();
final Task rootTask = task.getRootTask();
-
beginDeferResume();
// The LaunchActivityItem also contains process configuration, so the configuration change
// from WindowProcessController#setProcess can be deferred. The major reason is that if
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index de197a1..b4b8a74 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1262,6 +1262,37 @@
return null;
}
+ boolean pauseActivityIfNeeded(@Nullable ActivityRecord resuming, @NonNull String reason) {
+ if (!isLeafTask()) {
+ return false;
+ }
+
+ final int[] someActivityPaused = {0};
+ // Check if the direct child resumed activity in the leaf task needed to be paused if
+ // the leaf task is not a leaf task fragment.
+ if (!isLeafTaskFragment()) {
+ final ActivityRecord top = topRunningActivity();
+ final ActivityRecord resumedActivity = getResumedActivity();
+ if (resumedActivity != null && top.getTaskFragment() != this) {
+ // Pausing the resumed activity because it is occluded by other task fragment.
+ if (startPausing(false /* uiSleeping*/, resuming, reason)) {
+ someActivityPaused[0]++;
+ }
+ }
+ }
+
+ forAllLeafTaskFragments((taskFrag) -> {
+ final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
+ if (resumedActivity != null && !taskFrag.canBeResumed(resuming)) {
+ if (taskFrag.startPausing(false /* uiSleeping*/, resuming, reason)) {
+ someActivityPaused[0]++;
+ }
+ }
+ }, true /* traverseTopToBottom */);
+
+ return someActivityPaused[0] > 0;
+ }
+
void updateTaskMovement(boolean toTop, boolean toBottom, int position) {
EventLogTags.writeWmTaskMoved(mTaskId, getRootTaskId(), getDisplayId(), toTop ? 1 : 0,
position);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 9af12ad..ae794a8 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1271,27 +1271,9 @@
boolean pauseBackTasks(ActivityRecord resuming) {
final int[] someActivityPaused = {0};
forAllLeafTasks(leafTask -> {
- // Check if the direct child resumed activity in the leaf task needed to be paused if
- // the leaf task is not a leaf task fragment.
- if (!leafTask.isLeafTaskFragment()) {
- final ActivityRecord top = topRunningActivity();
- final ActivityRecord resumedActivity = leafTask.getResumedActivity();
- if (resumedActivity != null && top.getTaskFragment() != leafTask) {
- // Pausing the resumed activity because it is occluded by other task fragment.
- if (leafTask.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
- someActivityPaused[0]++;
- }
- }
+ if (leafTask.pauseActivityIfNeeded(resuming, "pauseBackTasks")) {
+ someActivityPaused[0]++;
}
-
- leafTask.forAllLeafTaskFragments((taskFrag) -> {
- final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
- if (resumedActivity != null && !taskFrag.canBeResumed(resuming)) {
- if (taskFrag.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
- someActivityPaused[0]++;
- }
- }
- }, true /* traverseTopToBottom */);
}, true /* traverseTopToBottom */);
return someActivityPaused[0] > 0;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 881fdec..bbafa25 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1614,13 +1614,6 @@
}
}
- // Take task snapshots before the animation so that we can capture IME before it gets
- // transferred. If transition is transient, IME won't be moved during the transition and
- // the tasks are still live, so we take the snapshot at the end of the transition instead.
- if (mTransientLaunches == null) {
- mController.mSnapshotController.onTransactionReady(mType, mTargets);
- }
-
// This is non-null only if display has changes. It handles the visible windows that don't
// need to be participated in the transition.
for (int i = 0; i < mTargetDisplays.size(); ++i) {
@@ -1671,6 +1664,16 @@
reportStartReasonsToLogger();
+ // Take snapshots for closing tasks/activities before the animation finished but after
+ // dispatching onTransitionReady, so IME (if there is) can be captured together and the
+ // time spent on snapshot won't delay the start of animation. Note that if this transition
+ // is transient (mTransientLaunches != null), the snapshot will be captured at the end of
+ // the transition, because IME won't move be moved during the transition and the tasks are
+ // still live.
+ if (mTransientLaunches == null) {
+ mController.mSnapshotController.onTransactionReady(mType, mTargets);
+ }
+
// Since we created root-leash but no longer reference it from core, release it now
info.releaseAnimSurfaces();