Loading core/java/android/window/WindowTokenClient.java +3 −11 Original line number Diff line number Diff line Loading @@ -95,11 +95,10 @@ public class WindowTokenClient extends IWindowToken.Stub { * @param newConfig the updated {@link Configuration} * @param newDisplayId the updated {@link android.view.Display} ID */ @AnyThread @MainThread @Override public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction postOnConfigurationChanged(newConfig, newDisplayId); onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */); } /** Loading Loading @@ -193,16 +192,9 @@ public class WindowTokenClient extends IWindowToken.Stub { } } @AnyThread @MainThread @Override public void onWindowTokenRemoved() { // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction mHandler.post(PooledLambda.obtainRunnable( WindowTokenClient::onWindowTokenRemovedInner, this).recycleOnUse()); } @MainThread private void onWindowTokenRemovedInner() { final Context context = mContextRef.get(); if (context != null) { context.destroy(); Loading services/core/java/com/android/server/wm/WindowContextListenerController.java +6 −12 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.IWindowToken; import android.app.servertransaction.WindowContextInfoChangeItem; import android.app.servertransaction.WindowContextWindowRemovalItem; import android.content.Context; import android.content.res.Configuration; import android.os.Bundle; Loading Loading @@ -329,12 +331,8 @@ class WindowContextListenerController { mLastReportedConfig.setTo(config); mLastReportedDisplay = displayId; try { // TODO(b/290876897): migrate to dispatch through wpc mClientToken.onConfigurationChanged(config, displayId); } catch (RemoteException e) { ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client."); } mWpc.scheduleClientTransactionItem(WindowContextInfoChangeItem.obtain( mClientToken.asBinder(), config, displayId)); mHasPendingConfiguration = false; } Loading @@ -359,12 +357,8 @@ class WindowContextListenerController { } } mDeathRecipient.unlinkToDeath(); try { // TODO(b/290876897): migrate to dispatch through wpc mClientToken.onWindowTokenRemoved(); } catch (RemoteException e) { ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client."); } mWpc.scheduleClientTransactionItem(WindowContextWindowRemovalItem.obtain( mClientToken.asBinder())); unregister(); } Loading services/core/java/com/android/server/wm/WindowProcessController.java +29 −7 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import android.app.ActivityThread; import android.app.BackgroundStartPrivileges; import android.app.IApplicationThread; import android.app.ProfilerInfo; import android.app.servertransaction.ClientTransactionItem; import android.app.servertransaction.ConfigurationChangeItem; import android.content.ComponentName; import android.content.Context; Loading Loading @@ -1584,9 +1585,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio resolvedConfig.seq = newParentConfig.seq; } void dispatchConfiguration(Configuration config) { void dispatchConfiguration(@NonNull Configuration config) { mHasPendingConfigurationChange = false; if (mThread == null) { final IApplicationThread thread = mThread; if (thread == null) { if (Build.IS_DEBUGGABLE && mHasImeService) { // TODO (b/135719017): Temporary log for debugging IME service. Slog.w(TAG_CONFIGURATION, "Unable to send config for IME proc " + mName Loading @@ -1611,10 +1613,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } } scheduleConfigurationChange(mThread, config); scheduleConfigurationChange(thread, config); } private void scheduleConfigurationChange(IApplicationThread thread, Configuration config) { private void scheduleConfigurationChange(@NonNull IApplicationThread thread, @NonNull Configuration config) { ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s new config %s", mName, config); if (Build.IS_DEBUGGABLE && mHasImeService) { Loading @@ -1622,11 +1625,30 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config); } mHasCachedConfiguration = false; scheduleClientTransactionItem(thread, ConfigurationChangeItem.obtain( config, mLastTopActivityDeviceId)); } @VisibleForTesting void scheduleClientTransactionItem(@NonNull ClientTransactionItem transactionItem) { final IApplicationThread thread = mThread; if (thread == null) { if (Build.IS_DEBUGGABLE) { Slog.w(TAG_CONFIGURATION, "Unable to send transaction to client proc " + mName + ": no app thread"); } return; } scheduleClientTransactionItem(thread, transactionItem); } private void scheduleClientTransactionItem(@NonNull IApplicationThread thread, @NonNull ClientTransactionItem transactionItem) { try { mAtm.getLifecycleManager().scheduleTransaction(thread, ConfigurationChangeItem.obtain(config, mLastTopActivityDeviceId)); mAtm.getLifecycleManager().scheduleTransaction(thread, transactionItem); } catch (Exception e) { Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change: " + mOwner, e); Slog.e(TAG_CONFIGURATION, "Failed to schedule ClientTransactionItem=" + transactionItem + " owner=" + mOwner, e); } } Loading services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java +45 −1 Original line number Diff line number Diff line Loading @@ -34,25 +34,33 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.MockitoAnnotations.initMocks; import android.app.ClientTransactionHandler; import android.app.IWindowToken; import android.app.servertransaction.ClientTransactionItem; import android.app.servertransaction.WindowContextInfoChangeItem; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import android.view.Display; import android.view.DisplayInfo; import android.window.WindowContextInfo; import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; /** Loading @@ -68,20 +76,52 @@ public class WindowContextListenerControllerTests extends WindowTestsBase { private static final int TEST_UID = 12345; private static final int ANOTHER_UID = 1000; @Mock private ClientTransactionHandler mHandler; private WindowProcessController mWpc; private final IBinder mClientToken = new Binder(); private WindowContainer<?> mContainer; private ArrayMap<IBinder, TestWindowTokenClient> mWindowTokenClientMap; @Before public void setUp() { initMocks(this); mController = new WindowContextListenerController(); mContainer = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent); // Make display on to verify configuration propagation. mDefaultDisplay.getDisplayInfo().state = STATE_ON; mDisplayContent.getDisplayInfo().state = STATE_ON; mWindowTokenClientMap = new ArrayMap<>(); mWpc = mSystemServicesTestRule.addProcess( DEFAULT_COMPONENT_PACKAGE_NAME, DEFAULT_COMPONENT_PACKAGE_NAME, 0 /* pid */, TEST_UID); // Mock the behaviors on ClientTransaction spyOn(mWpc); doAnswer(invocation -> { // Mock ActivityThread final Object[] args = invocation.getArguments(); final IBinder clientToken = (IBinder) args[0]; final WindowContextInfo info = (WindowContextInfo) args[1]; final TestWindowTokenClient client = mWindowTokenClientMap.get(clientToken); if (client != null) { // Can be null for mock client. client.onConfigurationChanged(info.getConfiguration(), info.getDisplayId()); } return null; }).when(mHandler).handleWindowContextInfoChanged(any(), any()); doAnswer(invocation -> { // Mock WindowProcessController final Object[] args = invocation.getArguments(); final ClientTransactionItem item = (ClientTransactionItem) args[0]; if (!(item instanceof WindowContextInfoChangeItem)) { return null; } final WindowContextInfoChangeItem infoChangeItem = (WindowContextInfoChangeItem) item; infoChangeItem.execute(mHandler, null, null); return null; }).when(mWpc).scheduleClientTransactionItem(any()); } @Test Loading Loading @@ -304,11 +344,15 @@ public class WindowContextListenerControllerTests extends WindowTestsBase { assertThat(clientToken.mDisplayId).isEqualTo(mDisplayContent.mDisplayId); } private static class TestWindowTokenClient extends IWindowToken.Stub { private class TestWindowTokenClient extends IWindowToken.Stub { private Configuration mConfiguration; private int mDisplayId; private boolean mRemoved; TestWindowTokenClient() { mWindowTokenClientMap.put(asBinder(), this); } @Override public void onConfigurationChanged(Configuration configuration, int displayId) { mConfiguration = configuration; Loading Loading
core/java/android/window/WindowTokenClient.java +3 −11 Original line number Diff line number Diff line Loading @@ -95,11 +95,10 @@ public class WindowTokenClient extends IWindowToken.Stub { * @param newConfig the updated {@link Configuration} * @param newDisplayId the updated {@link android.view.Display} ID */ @AnyThread @MainThread @Override public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction postOnConfigurationChanged(newConfig, newDisplayId); onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */); } /** Loading Loading @@ -193,16 +192,9 @@ public class WindowTokenClient extends IWindowToken.Stub { } } @AnyThread @MainThread @Override public void onWindowTokenRemoved() { // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction mHandler.post(PooledLambda.obtainRunnable( WindowTokenClient::onWindowTokenRemovedInner, this).recycleOnUse()); } @MainThread private void onWindowTokenRemovedInner() { final Context context = mContextRef.get(); if (context != null) { context.destroy(); Loading
services/core/java/com/android/server/wm/WindowContextListenerController.java +6 −12 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.IWindowToken; import android.app.servertransaction.WindowContextInfoChangeItem; import android.app.servertransaction.WindowContextWindowRemovalItem; import android.content.Context; import android.content.res.Configuration; import android.os.Bundle; Loading Loading @@ -329,12 +331,8 @@ class WindowContextListenerController { mLastReportedConfig.setTo(config); mLastReportedDisplay = displayId; try { // TODO(b/290876897): migrate to dispatch through wpc mClientToken.onConfigurationChanged(config, displayId); } catch (RemoteException e) { ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client."); } mWpc.scheduleClientTransactionItem(WindowContextInfoChangeItem.obtain( mClientToken.asBinder(), config, displayId)); mHasPendingConfiguration = false; } Loading @@ -359,12 +357,8 @@ class WindowContextListenerController { } } mDeathRecipient.unlinkToDeath(); try { // TODO(b/290876897): migrate to dispatch through wpc mClientToken.onWindowTokenRemoved(); } catch (RemoteException e) { ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client."); } mWpc.scheduleClientTransactionItem(WindowContextWindowRemovalItem.obtain( mClientToken.asBinder())); unregister(); } Loading
services/core/java/com/android/server/wm/WindowProcessController.java +29 −7 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import android.app.ActivityThread; import android.app.BackgroundStartPrivileges; import android.app.IApplicationThread; import android.app.ProfilerInfo; import android.app.servertransaction.ClientTransactionItem; import android.app.servertransaction.ConfigurationChangeItem; import android.content.ComponentName; import android.content.Context; Loading Loading @@ -1584,9 +1585,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio resolvedConfig.seq = newParentConfig.seq; } void dispatchConfiguration(Configuration config) { void dispatchConfiguration(@NonNull Configuration config) { mHasPendingConfigurationChange = false; if (mThread == null) { final IApplicationThread thread = mThread; if (thread == null) { if (Build.IS_DEBUGGABLE && mHasImeService) { // TODO (b/135719017): Temporary log for debugging IME service. Slog.w(TAG_CONFIGURATION, "Unable to send config for IME proc " + mName Loading @@ -1611,10 +1613,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } } scheduleConfigurationChange(mThread, config); scheduleConfigurationChange(thread, config); } private void scheduleConfigurationChange(IApplicationThread thread, Configuration config) { private void scheduleConfigurationChange(@NonNull IApplicationThread thread, @NonNull Configuration config) { ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s new config %s", mName, config); if (Build.IS_DEBUGGABLE && mHasImeService) { Loading @@ -1622,11 +1625,30 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config); } mHasCachedConfiguration = false; scheduleClientTransactionItem(thread, ConfigurationChangeItem.obtain( config, mLastTopActivityDeviceId)); } @VisibleForTesting void scheduleClientTransactionItem(@NonNull ClientTransactionItem transactionItem) { final IApplicationThread thread = mThread; if (thread == null) { if (Build.IS_DEBUGGABLE) { Slog.w(TAG_CONFIGURATION, "Unable to send transaction to client proc " + mName + ": no app thread"); } return; } scheduleClientTransactionItem(thread, transactionItem); } private void scheduleClientTransactionItem(@NonNull IApplicationThread thread, @NonNull ClientTransactionItem transactionItem) { try { mAtm.getLifecycleManager().scheduleTransaction(thread, ConfigurationChangeItem.obtain(config, mLastTopActivityDeviceId)); mAtm.getLifecycleManager().scheduleTransaction(thread, transactionItem); } catch (Exception e) { Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change: " + mOwner, e); Slog.e(TAG_CONFIGURATION, "Failed to schedule ClientTransactionItem=" + transactionItem + " owner=" + mOwner, e); } } Loading
services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java +45 −1 Original line number Diff line number Diff line Loading @@ -34,25 +34,33 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.MockitoAnnotations.initMocks; import android.app.ClientTransactionHandler; import android.app.IWindowToken; import android.app.servertransaction.ClientTransactionItem; import android.app.servertransaction.WindowContextInfoChangeItem; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import android.view.Display; import android.view.DisplayInfo; import android.window.WindowContextInfo; import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; /** Loading @@ -68,20 +76,52 @@ public class WindowContextListenerControllerTests extends WindowTestsBase { private static final int TEST_UID = 12345; private static final int ANOTHER_UID = 1000; @Mock private ClientTransactionHandler mHandler; private WindowProcessController mWpc; private final IBinder mClientToken = new Binder(); private WindowContainer<?> mContainer; private ArrayMap<IBinder, TestWindowTokenClient> mWindowTokenClientMap; @Before public void setUp() { initMocks(this); mController = new WindowContextListenerController(); mContainer = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent); // Make display on to verify configuration propagation. mDefaultDisplay.getDisplayInfo().state = STATE_ON; mDisplayContent.getDisplayInfo().state = STATE_ON; mWindowTokenClientMap = new ArrayMap<>(); mWpc = mSystemServicesTestRule.addProcess( DEFAULT_COMPONENT_PACKAGE_NAME, DEFAULT_COMPONENT_PACKAGE_NAME, 0 /* pid */, TEST_UID); // Mock the behaviors on ClientTransaction spyOn(mWpc); doAnswer(invocation -> { // Mock ActivityThread final Object[] args = invocation.getArguments(); final IBinder clientToken = (IBinder) args[0]; final WindowContextInfo info = (WindowContextInfo) args[1]; final TestWindowTokenClient client = mWindowTokenClientMap.get(clientToken); if (client != null) { // Can be null for mock client. client.onConfigurationChanged(info.getConfiguration(), info.getDisplayId()); } return null; }).when(mHandler).handleWindowContextInfoChanged(any(), any()); doAnswer(invocation -> { // Mock WindowProcessController final Object[] args = invocation.getArguments(); final ClientTransactionItem item = (ClientTransactionItem) args[0]; if (!(item instanceof WindowContextInfoChangeItem)) { return null; } final WindowContextInfoChangeItem infoChangeItem = (WindowContextInfoChangeItem) item; infoChangeItem.execute(mHandler, null, null); return null; }).when(mWpc).scheduleClientTransactionItem(any()); } @Test Loading Loading @@ -304,11 +344,15 @@ public class WindowContextListenerControllerTests extends WindowTestsBase { assertThat(clientToken.mDisplayId).isEqualTo(mDisplayContent.mDisplayId); } private static class TestWindowTokenClient extends IWindowToken.Stub { private class TestWindowTokenClient extends IWindowToken.Stub { private Configuration mConfiguration; private int mDisplayId; private boolean mRemoved; TestWindowTokenClient() { mWindowTokenClientMap.put(asBinder(), this); } @Override public void onConfigurationChanged(Configuration configuration, int displayId) { mConfiguration = configuration; Loading