diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-01-15 22:06:37 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-01-15 22:06:37 +0000 |
commit | 6801b363d3445b712135e182e18c06494b2743c6 (patch) | |
tree | 18a4f99b68adad3d0e55f51e87852dd15547d7e5 | |
parent | 186b81dc18cb6d5768ab02e6a873a6668c3eeb38 (diff) | |
parent | 785ee1a55c84263c64c5aaa9959287a4efb0b34f (diff) | |
download | base-6801b363d3445b712135e182e18c06494b2743c6.tar.gz |
Snap for 8084085 from 785ee1a55c84263c64c5aaa9959287a4efb0b34f to sc-v2-release
Change-Id: If9982c1586e37bf5863c0777d2cbba45a0437b43
5 files changed, 91 insertions, 15 deletions
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index fc1884a41653..06af6b180d07 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -311,6 +311,14 @@ class ContextImpl extends Context { @ContextType private int mContextType; + /** + * {@code true} to indicate that the {@link Context} owns the {@link #getWindowContextToken()} + * and is responsible for detaching the token when the Context is released. + * + * @see #finalize() + */ + private boolean mOwnsToken = false; + @GuardedBy("mSync") private File mDatabasesDir; @GuardedBy("mSync") @@ -2979,7 +2987,7 @@ class ContextImpl extends Context { // WindowContainer. We should detach from WindowContainer when the Context is finalized // if this Context is not a WindowContext. WindowContext finalization is handled in // WindowContext class. - if (mToken instanceof WindowTokenClient && mContextType != CONTEXT_TYPE_WINDOW_CONTEXT) { + if (mToken instanceof WindowTokenClient && mOwnsToken) { ((WindowTokenClient) mToken).detachFromWindowContainerIfNeeded(); } super.finalize(); @@ -3010,6 +3018,7 @@ class ContextImpl extends Context { token.attachContext(context); token.attachToDisplayContent(displayId); context.mContextType = CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI; + context.mOwnsToken = true; return context; } diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java index 4ba7ef26e9cb..547535d90e5a 100644 --- a/core/java/android/window/WindowTokenClient.java +++ b/core/java/android/window/WindowTokenClient.java @@ -19,9 +19,10 @@ import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded; import static android.window.ConfigurationHelper.isDifferentDisplay; import static android.window.ConfigurationHelper.shouldUpdateResources; +import android.annotation.BinderThread; +import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityThread; import android.app.IWindowToken; import android.app.ResourcesManager; import android.content.Context; @@ -30,7 +31,9 @@ import android.inputmethodservice.AbstractInputMethodService; import android.os.Build; import android.os.Bundle; import android.os.Debug; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.view.IWindowManager; @@ -71,6 +74,8 @@ public class WindowTokenClient extends IWindowToken.Stub { private boolean mAttachToWindowContainer; + private final Handler mHandler = new Handler(Looper.getMainLooper()); + /** * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient} * can only attach one {@link Context}. @@ -132,7 +137,8 @@ public class WindowTokenClient extends IWindowToken.Stub { if (configuration == null) { return false; } - onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */); + mHandler.post(() -> onConfigurationChanged(configuration, displayId, + false /* shouldReportConfigChange */)); mAttachToWindowContainer = true; return true; } catch (RemoteException e) { @@ -179,9 +185,11 @@ public class WindowTokenClient extends IWindowToken.Stub { * @param newConfig the updated {@link Configuration} * @param newDisplayId the updated {@link android.view.Display} ID */ + @BinderThread @Override public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { - onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */); + mHandler.post(() -> onConfigurationChanged(newConfig, newDisplayId, + true /* shouldReportConfigChange */)); } // TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService @@ -192,6 +200,7 @@ public class WindowTokenClient extends IWindowToken.Stub { * Similar to {@link #onConfigurationChanged(Configuration, int)}, but adds a flag to control * whether to dispatch configuration update or not. */ + @MainThread @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public void onConfigurationChanged(Configuration newConfig, int newDisplayId, boolean shouldReportConfigChange) { @@ -217,16 +226,14 @@ public class WindowTokenClient extends IWindowToken.Stub { if (shouldReportConfigChange && context instanceof WindowContext) { final WindowContext windowContext = (WindowContext) context; - ActivityThread.currentActivityThread().getHandler().post( - () -> windowContext.dispatchConfigurationChanged(newConfig)); + windowContext.dispatchConfigurationChanged(newConfig); } final int diff = mConfiguration.diffPublicOnly(newConfig); if (shouldReportConfigChange && diff != 0 && context instanceof WindowProviderService) { final WindowProviderService windowProviderService = (WindowProviderService) context; - ActivityThread.currentActivityThread().getHandler().post( - () -> windowProviderService.onConfigurationChanged(newConfig)); + windowProviderService.onConfigurationChanged(newConfig); } freeTextLayoutCachesIfNeeded(diff); if (mShouldDumpConfigForIme) { @@ -248,12 +255,15 @@ public class WindowTokenClient extends IWindowToken.Stub { } } + @BinderThread @Override public void onWindowTokenRemoved() { - final Context context = mContextRef.get(); - if (context != null) { - context.destroy(); - mContextRef.clear(); - } + mHandler.post(() -> { + final Context context = mContextRef.get(); + if (context != null) { + context.destroy(); + mContextRef.clear(); + } + }); } } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 0f26f57e2155..89b33590ab35 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -783,7 +783,7 @@ public class ZygoteInit { "--setuid=1000", "--setgid=1000", "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023," - + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011", + + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011,3012", "--capabilities=" + capabilities + "," + capabilities, "--nice-name=system_server", "--runtime-args", diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index c7fdefc412cc..123ca889c73e 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -22,6 +22,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANI import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.Configuration; import android.graphics.Rect; @@ -497,6 +498,23 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr return null; } + private boolean shouldSendEventWhenTaskInvisible(@NonNull Task task, + @NonNull PendingTaskFragmentEvent event) { + final TaskFragmentOrganizerState state = + mTaskFragmentOrganizerState.get(event.mTaskFragmentOrg.asBinder()); + final TaskFragmentInfo lastInfo = state.mLastSentTaskFragmentInfos.get(event.mTaskFragment); + final TaskFragmentInfo info = event.mTaskFragment.getTaskFragmentInfo(); + // Send an info changed callback if this event is for the last activities to finish in a + // Task so that the {@link TaskFragmentOrganizer} can delete this TaskFragment. Otherwise, + // the Task may be removed before it becomes visible again to send this event because it no + // longer has activities. As a result, the organizer will never get this info changed event + // and will not delete the TaskFragment because the organizer thinks the TaskFragment still + // has running activities. + return event.mEventType == PendingTaskFragmentEvent.EVENT_INFO_CHANGED + && task.topRunningActivity() == null && lastInfo != null + && lastInfo.getRunningActivityCount() > 0 && info.getRunningActivityCount() == 0; + } + void dispatchPendingEvents() { if (mAtmService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred() || mPendingTaskFragmentEvents.isEmpty()) { @@ -510,7 +528,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i); final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null; if (task != null && (task.lastActiveTime <= event.mDeferTime - || !isTaskVisible(task, visibleTasks, invisibleTasks))) { + || !(isTaskVisible(task, visibleTasks, invisibleTasks) + || shouldSendEventWhenTaskInvisible(task, event)))) { // Defer sending events to the TaskFragment until the host task is active again. event.mDeferTime = task.lastActiveTime; continue; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index dcaf9d7ae434..f8c7207cefa7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -21,14 +21,17 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.testing.Assert.assertThrows; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import android.content.Intent; @@ -471,6 +474,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) .setParentTask(task) .setOrganizer(mOrganizer) + .setFragmentToken(mFragmentToken) .build(); // Mock the task to invisible @@ -485,4 +489,38 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { // Verifies that event was not sent verify(mOrganizer, never()).onTaskFragmentInfoChanged(any()); } + + /** + * Tests that a task fragment info changed event is still sent if the task is invisible only + * when the info changed event is because of the last activity in a task finishing. + */ + @Test + public void testLastPendingTaskFragmentInfoChangedEventOfInvisibleTaskSent() { + // Create a TaskFragment with an activity, all within a parent task + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setOrganizer(mOrganizer) + .setFragmentToken(mFragmentToken) + .setCreateParentTask() + .createActivityCount(1) + .build(); + final Task parentTask = taskFragment.getTask(); + final ActivityRecord activity = taskFragment.getTopNonFinishingActivity(); + assertTrue(parentTask.shouldBeVisible(null)); + + // Dispatch pending info changed event from creating the activity + mController.registerOrganizer(mIOrganizer); + taskFragment.mTaskFragmentAppearedSent = true; + mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); + mController.dispatchPendingEvents(); + + // Finish the activity and verify that the task is invisible + activity.finishing = true; + assertFalse(parentTask.shouldBeVisible(null)); + + // Verify the info changed callback still occurred despite the task being invisible + reset(mOrganizer); + mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); + mController.dispatchPendingEvents(); + verify(mOrganizer).onTaskFragmentInfoChanged(any()); + } } |