Loading core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +106 −70 Original line number Diff line number Diff line Loading @@ -34,9 +34,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.annotation.NonNull; Loading Loading @@ -67,7 +69,6 @@ import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.util.DisplayMetrics; Loading @@ -90,7 +91,6 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; Loading Loading @@ -123,9 +123,7 @@ public class ActivityThreadTest { @Rule(order = 1) public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); @Mock private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener; private ActivityWindowInfoListener mActivityWindowInfoListener; private WindowTokenClientController mOriginalWindowTokenClientController; private Configuration mOriginalAppConfig; Loading @@ -140,6 +138,7 @@ public class ActivityThreadTest { mOriginalWindowTokenClientController = WindowTokenClientController.getInstance(); mOriginalAppConfig = new Configuration(ActivityThread.currentActivityThread() .getConfiguration()); mActivityWindowInfoListener = spy(new ActivityWindowInfoListener()); } @After Loading Loading @@ -808,66 +807,73 @@ public class ActivityThreadTest { @Test public void testActivityWindowInfoChanged_activityLaunch() { mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG); ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener( mActivityWindowInfoListener); final Activity activity = mActivityTestRule.launchActivity(new Intent()); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); mActivityWindowInfoListener.await(); final ActivityClientRecord activityClientRecord = getActivityClientRecord(activity); verify(mActivityWindowInfoListener).accept(activityClientRecord.token, // In case the system change the window after launch, there can be more than one callback. verify(mActivityWindowInfoListener, atLeastOnce()).accept(activityClientRecord.token, activityClientRecord.getActivityWindowInfo()); } @Test public void testActivityWindowInfoChanged_activityRelaunch() throws RemoteException { public void testActivityWindowInfoChanged_activityRelaunch() { mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG); ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener( mActivityWindowInfoListener); final Activity activity = mActivityTestRule.launchActivity(new Intent()); final IApplicationThread appThread = activity.getActivityThread().getApplicationThread(); appThread.scheduleTransaction(newRelaunchResumeTransaction(activity)); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); mActivityWindowInfoListener.await(); final ActivityClientRecord activityClientRecord = getActivityClientRecord(activity); // Run on main thread to avoid racing from updating from window relayout. final ActivityThread activityThread = activity.getActivityThread(); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { // Try relaunch with the same ActivityWindowInfo clearInvocations(mActivityWindowInfoListener); activityThread.executeTransaction(newRelaunchResumeTransaction(activity)); // The same ActivityWindowInfo won't trigger duplicated callback. verify(mActivityWindowInfoListener).accept(activityClientRecord.token, verify(mActivityWindowInfoListener, never()).accept(activityClientRecord.token, activityClientRecord.getActivityWindowInfo()); // Try relaunch with different ActivityWindowInfo final Configuration currentConfig = activity.getResources().getConfiguration(); final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo(); activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000), final ActivityWindowInfo newInfo = new ActivityWindowInfo(); newInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000), new Rect(0, 0, 1000, 1000)); final ActivityRelaunchItem relaunchItem = ActivityRelaunchItem.obtain( activity.getActivityToken(), null, null, 0, new MergedConfiguration(currentConfig, currentConfig), false /* preserveWindow */, activityWindowInfo); false /* preserveWindow */, newInfo); final ClientTransaction transaction = newTransaction(activity); transaction.addTransactionItem(relaunchItem); appThread.scheduleTransaction(transaction); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); verify(mActivityWindowInfoListener).accept(activityClientRecord.token, activityWindowInfo); clearInvocations(mActivityWindowInfoListener); activityThread.executeTransaction(transaction); // Trigger callback with a different ActivityWindowInfo verify(mActivityWindowInfoListener).accept(activityClientRecord.token, newInfo); }); } @Test public void testActivityWindowInfoChanged_activityConfigurationChanged() throws RemoteException { public void testActivityWindowInfoChanged_activityConfigurationChanged() { mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG); ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener( mActivityWindowInfoListener); final Activity activity = mActivityTestRule.launchActivity(new Intent()); final IApplicationThread appThread = activity.getActivityThread().getApplicationThread(); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); mActivityWindowInfoListener.await(); clearInvocations(mActivityWindowInfoListener); final Configuration config = new Configuration(activity.getResources().getConfiguration()); final ActivityThread activityThread = activity.getActivityThread(); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { // Trigger callback with different ActivityWindowInfo final Configuration config = new Configuration(activity.getResources() .getConfiguration()); config.seq++; final Rect taskBounds = new Rect(0, 0, 1000, 2000); final Rect taskFragmentBounds = new Rect(0, 0, 1000, 1000); Loading @@ -878,26 +884,30 @@ public class ActivityThreadTest { activity.getActivityToken(), config, activityWindowInfo); final ClientTransaction transaction = newTransaction(activity); transaction.addTransactionItem(activityConfigurationChangeItem); appThread.scheduleTransaction(transaction); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); clearInvocations(mActivityWindowInfoListener); activityThread.executeTransaction(transaction); // Trigger callback with a different ActivityWindowInfo verify(mActivityWindowInfoListener).accept(activity.getActivityToken(), activityWindowInfo); clearInvocations(mActivityWindowInfoListener); final ActivityWindowInfo activityWindowInfo2 = new ActivityWindowInfo(); activityWindowInfo2.set(true /* isEmbedded */, taskBounds, taskFragmentBounds); // Try callback with the same ActivityWindowInfo final ActivityWindowInfo activityWindowInfo2 = new ActivityWindowInfo(activityWindowInfo); config.seq++; final ActivityConfigurationChangeItem activityConfigurationChangeItem2 = ActivityConfigurationChangeItem.obtain( activity.getActivityToken(), config, activityWindowInfo2); final ClientTransaction transaction2 = newTransaction(activity); transaction2.addTransactionItem(activityConfigurationChangeItem2); appThread.scheduleTransaction(transaction); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); clearInvocations(mActivityWindowInfoListener); activityThread.executeTransaction(transaction); // The same ActivityWindowInfo won't trigger duplicated callback. verify(mActivityWindowInfoListener, never()).accept(any(), any()); }); } /** Loading Loading @@ -958,10 +968,12 @@ public class ActivityThreadTest { @NonNull private static ClientTransaction newRelaunchResumeTransaction(@NonNull Activity activity) { final Configuration currentConfig = activity.getResources().getConfiguration(); final ActivityWindowInfo activityWindowInfo = getActivityClientRecord(activity) .getActivityWindowInfo(); final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain( activity.getActivityToken(), null, null, 0, new MergedConfiguration(currentConfig, currentConfig), false /* preserveWindow */, new ActivityWindowInfo()); false /* preserveWindow */, activityWindowInfo); final ResumeActivityItem resumeStateRequest = ResumeActivityItem.obtain(activity.getActivityToken(), true /* isForward */, false /* shouldSendCompatFakeFocus*/); Loading Loading @@ -1127,4 +1139,28 @@ public class ActivityThreadTest { return mPipEnterSkipped; } } public static class ActivityWindowInfoListener implements BiConsumer<IBinder, ActivityWindowInfo> { CountDownLatch mCallbackLatch = new CountDownLatch(1); @Override public void accept(@NonNull IBinder activityToken, @NonNull ActivityWindowInfo activityWindowInfo) { mCallbackLatch.countDown(); } /** * When the test is expecting to receive a callback, waits until the callback is triggered. */ void await() { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); try { mCallbackLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } Loading
core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +106 −70 Original line number Diff line number Diff line Loading @@ -34,9 +34,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.annotation.NonNull; Loading Loading @@ -67,7 +69,6 @@ import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.util.DisplayMetrics; Loading @@ -90,7 +91,6 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; Loading Loading @@ -123,9 +123,7 @@ public class ActivityThreadTest { @Rule(order = 1) public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); @Mock private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener; private ActivityWindowInfoListener mActivityWindowInfoListener; private WindowTokenClientController mOriginalWindowTokenClientController; private Configuration mOriginalAppConfig; Loading @@ -140,6 +138,7 @@ public class ActivityThreadTest { mOriginalWindowTokenClientController = WindowTokenClientController.getInstance(); mOriginalAppConfig = new Configuration(ActivityThread.currentActivityThread() .getConfiguration()); mActivityWindowInfoListener = spy(new ActivityWindowInfoListener()); } @After Loading Loading @@ -808,66 +807,73 @@ public class ActivityThreadTest { @Test public void testActivityWindowInfoChanged_activityLaunch() { mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG); ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener( mActivityWindowInfoListener); final Activity activity = mActivityTestRule.launchActivity(new Intent()); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); mActivityWindowInfoListener.await(); final ActivityClientRecord activityClientRecord = getActivityClientRecord(activity); verify(mActivityWindowInfoListener).accept(activityClientRecord.token, // In case the system change the window after launch, there can be more than one callback. verify(mActivityWindowInfoListener, atLeastOnce()).accept(activityClientRecord.token, activityClientRecord.getActivityWindowInfo()); } @Test public void testActivityWindowInfoChanged_activityRelaunch() throws RemoteException { public void testActivityWindowInfoChanged_activityRelaunch() { mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG); ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener( mActivityWindowInfoListener); final Activity activity = mActivityTestRule.launchActivity(new Intent()); final IApplicationThread appThread = activity.getActivityThread().getApplicationThread(); appThread.scheduleTransaction(newRelaunchResumeTransaction(activity)); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); mActivityWindowInfoListener.await(); final ActivityClientRecord activityClientRecord = getActivityClientRecord(activity); // Run on main thread to avoid racing from updating from window relayout. final ActivityThread activityThread = activity.getActivityThread(); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { // Try relaunch with the same ActivityWindowInfo clearInvocations(mActivityWindowInfoListener); activityThread.executeTransaction(newRelaunchResumeTransaction(activity)); // The same ActivityWindowInfo won't trigger duplicated callback. verify(mActivityWindowInfoListener).accept(activityClientRecord.token, verify(mActivityWindowInfoListener, never()).accept(activityClientRecord.token, activityClientRecord.getActivityWindowInfo()); // Try relaunch with different ActivityWindowInfo final Configuration currentConfig = activity.getResources().getConfiguration(); final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo(); activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000), final ActivityWindowInfo newInfo = new ActivityWindowInfo(); newInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000), new Rect(0, 0, 1000, 1000)); final ActivityRelaunchItem relaunchItem = ActivityRelaunchItem.obtain( activity.getActivityToken(), null, null, 0, new MergedConfiguration(currentConfig, currentConfig), false /* preserveWindow */, activityWindowInfo); false /* preserveWindow */, newInfo); final ClientTransaction transaction = newTransaction(activity); transaction.addTransactionItem(relaunchItem); appThread.scheduleTransaction(transaction); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); verify(mActivityWindowInfoListener).accept(activityClientRecord.token, activityWindowInfo); clearInvocations(mActivityWindowInfoListener); activityThread.executeTransaction(transaction); // Trigger callback with a different ActivityWindowInfo verify(mActivityWindowInfoListener).accept(activityClientRecord.token, newInfo); }); } @Test public void testActivityWindowInfoChanged_activityConfigurationChanged() throws RemoteException { public void testActivityWindowInfoChanged_activityConfigurationChanged() { mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG); ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener( mActivityWindowInfoListener); final Activity activity = mActivityTestRule.launchActivity(new Intent()); final IApplicationThread appThread = activity.getActivityThread().getApplicationThread(); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); mActivityWindowInfoListener.await(); clearInvocations(mActivityWindowInfoListener); final Configuration config = new Configuration(activity.getResources().getConfiguration()); final ActivityThread activityThread = activity.getActivityThread(); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { // Trigger callback with different ActivityWindowInfo final Configuration config = new Configuration(activity.getResources() .getConfiguration()); config.seq++; final Rect taskBounds = new Rect(0, 0, 1000, 2000); final Rect taskFragmentBounds = new Rect(0, 0, 1000, 1000); Loading @@ -878,26 +884,30 @@ public class ActivityThreadTest { activity.getActivityToken(), config, activityWindowInfo); final ClientTransaction transaction = newTransaction(activity); transaction.addTransactionItem(activityConfigurationChangeItem); appThread.scheduleTransaction(transaction); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); clearInvocations(mActivityWindowInfoListener); activityThread.executeTransaction(transaction); // Trigger callback with a different ActivityWindowInfo verify(mActivityWindowInfoListener).accept(activity.getActivityToken(), activityWindowInfo); clearInvocations(mActivityWindowInfoListener); final ActivityWindowInfo activityWindowInfo2 = new ActivityWindowInfo(); activityWindowInfo2.set(true /* isEmbedded */, taskBounds, taskFragmentBounds); // Try callback with the same ActivityWindowInfo final ActivityWindowInfo activityWindowInfo2 = new ActivityWindowInfo(activityWindowInfo); config.seq++; final ActivityConfigurationChangeItem activityConfigurationChangeItem2 = ActivityConfigurationChangeItem.obtain( activity.getActivityToken(), config, activityWindowInfo2); final ClientTransaction transaction2 = newTransaction(activity); transaction2.addTransactionItem(activityConfigurationChangeItem2); appThread.scheduleTransaction(transaction); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); clearInvocations(mActivityWindowInfoListener); activityThread.executeTransaction(transaction); // The same ActivityWindowInfo won't trigger duplicated callback. verify(mActivityWindowInfoListener, never()).accept(any(), any()); }); } /** Loading Loading @@ -958,10 +968,12 @@ public class ActivityThreadTest { @NonNull private static ClientTransaction newRelaunchResumeTransaction(@NonNull Activity activity) { final Configuration currentConfig = activity.getResources().getConfiguration(); final ActivityWindowInfo activityWindowInfo = getActivityClientRecord(activity) .getActivityWindowInfo(); final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain( activity.getActivityToken(), null, null, 0, new MergedConfiguration(currentConfig, currentConfig), false /* preserveWindow */, new ActivityWindowInfo()); false /* preserveWindow */, activityWindowInfo); final ResumeActivityItem resumeStateRequest = ResumeActivityItem.obtain(activity.getActivityToken(), true /* isForward */, false /* shouldSendCompatFakeFocus*/); Loading Loading @@ -1127,4 +1139,28 @@ public class ActivityThreadTest { return mPipEnterSkipped; } } public static class ActivityWindowInfoListener implements BiConsumer<IBinder, ActivityWindowInfo> { CountDownLatch mCallbackLatch = new CountDownLatch(1); @Override public void accept(@NonNull IBinder activityToken, @NonNull ActivityWindowInfo activityWindowInfo) { mCallbackLatch.countDown(); } /** * When the test is expecting to receive a callback, waits until the callback is triggered. */ void await() { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); try { mCallbackLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }