cts: test replacing window functionality
bug: 26324082
bug: 19225708
Change-Id: Ia4e89d45ce3e260de65bd7b8b25049e6828c81fb
diff --git a/hostsidetests/services/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
index 995649f..7e1662d 100755
--- a/hostsidetests/services/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
@@ -24,6 +24,15 @@
android:resizeable="true"
android:exported="true"
/>
+ <activity android:name=".NoRelaunchActivity"
+ android:resizeable="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|layoutDirection"
+ android:exported="true"
+ />
+ <activity android:name=".SlowCreateActivity"
+ android:resizeable="true"
+ android:exported="true"
+ />
<activity android:name=".LaunchToSideActivity"
android:resizeable="true"
android:exported="true"
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/NoRelaunchActivity.java b/hostsidetests/services/activitymanager/app/src/android/server/app/NoRelaunchActivity.java
new file mode 100644
index 0000000..37963de
--- /dev/null
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/NoRelaunchActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.app;
+
+import android.app.Activity;
+
+public class NoRelaunchActivity extends Activity {
+}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
new file mode 100644
index 0000000..481f5be
--- /dev/null
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SlowCreateActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ try {
+ Thread.sleep(2000);
+ } catch(InterruptedException e) {}
+ super.onCreate(savedInstanceState);
+ }
+}
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityAndWindowManagersState.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityAndWindowManagersState.java
index 5926698..fc60b78 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityAndWindowManagersState.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityAndWindowManagersState.java
@@ -29,8 +29,20 @@
private WindowManagerState mWmState = new WindowManagerState();
void computeState(ITestDevice device) throws Exception {
+ computeState(device, true);
+ }
+
+ void computeState(ITestDevice device, boolean visibleOnly) throws Exception {
mAmState.computeState(device);
- mWmState.computeState(device);
+ mWmState.computeState(device, visibleOnly);
+ }
+
+ ActivityManagerState getAmState() {
+ return mAmState;
+ }
+
+ WindowManagerState getWmState() {
+ return mWmState;
}
void assertSanity() throws Exception {
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java
new file mode 100644
index 0000000..a4ec55b
--- /dev/null
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.cts;
+
+import java.lang.Exception;
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import junit.framework.Assert;
+
+import static com.android.ddmlib.Log.LogLevel.INFO;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+
+public class ActivityManagerReplaceWindowTests extends ActivityManagerTestBase {
+
+ private static final String SLOW_CREATE_ACTIVITY_NAME = "SlowCreateActivity";
+ private static final String NO_RELAUNCH_ACTIVITY_NAME = "NoRelaunchActivity";
+
+ private static final String AM_FORCE_STOP_TEST = "am force-stop android.server.app";
+ private static final String AM_MOVE_TASK = "am stack movetask ";
+
+ private List<String> mTempWindowTokens = new ArrayList();
+
+ @Override
+ protected void tearDown() {
+ try {
+ mDevice.executeShellCommand(AM_FORCE_STOP_TEST);
+ } catch (DeviceNotAvailableException e) {
+ }
+ }
+
+ public void testReplaceWindow_Dock_Relaunch() throws Exception {
+ testReplaceWindow_Dock(true);
+ }
+
+ public void testReplaceWindow_Dock_NoRelaunch() throws Exception {
+ testReplaceWindow_Dock(false);
+ }
+
+ private void testReplaceWindow_Dock(boolean relaunch) throws Exception {
+ final String activityName =
+ relaunch ? SLOW_CREATE_ACTIVITY_NAME : NO_RELAUNCH_ACTIVITY_NAME;
+ final String windowName = getWindowName(activityName);
+ final String amStartCmd = getAmStartCmd(activityName);
+
+ mDevice.executeShellCommand(amStartCmd);
+
+ // Sleep 2 seconds, then check if the window is started properly.
+ // SlowCreateActivity will do a sleep inside its onCreate() to simulate a
+ // slow-starting app. So instead of relying on WindowManagerState's
+ // retrying mechanism, we do an explicit sleep to avoid excess spews
+ // from WindowManagerState.
+ if (SLOW_CREATE_ACTIVITY_NAME.equals(activityName)) {
+ Thread.sleep(2000);
+ }
+
+ CLog.logAndDisplay(INFO, "==========Before Docking========");
+ final String oldToken = getFocusedWindowToken(windowName, true);
+
+ // Move to docked stack
+ final int taskId = getActivityTaskId(activityName);
+ final String cmd = AM_MOVE_TASK + taskId + " " + DOCKED_STACK_ID + " true";
+ mDevice.executeShellCommand(cmd);
+
+ // Sleep 5 seconds, then check if the window is replaced properly.
+ Thread.sleep(5000);
+
+ CLog.logAndDisplay(INFO, "==========After Docking========");
+ final String newToken = getFocusedWindowToken(windowName, false);
+
+ if (relaunch) {
+ Assert.assertFalse("Window not replaced after relaunch.", oldToken.equals(newToken));
+ } else {
+ Assert.assertEquals("Window replaced without relaunch.", oldToken, newToken);
+ }
+ }
+
+ private String getFocusedWindowToken(String windowName, boolean visibleOnly)
+ throws Exception {
+ mAmWmState.computeState(mDevice, visibleOnly);
+
+ mAmWmState.assertSanity();
+
+ mAmWmState.assertFocusedWindow("Test window must be the front window.",
+ windowName);
+
+ mAmWmState.getWmState().getMatchingWindowTokens(windowName, mTempWindowTokens);
+
+ Assert.assertEquals("Should have exactly one window for the activity.",
+ 1, mTempWindowTokens.size());
+
+ return mTempWindowTokens.get(0);
+ }
+}
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
index 65c375e..953d185 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
@@ -59,6 +59,14 @@
private HashSet<String> mAvailableFeatures;
+ protected static String getAmStartCmd(final String activityName) {
+ return "am start -n android.server.app/." + activityName;
+ }
+
+ protected static String getWindowName(final String activityName) {
+ return "android.server.app/android.server.app." + activityName;
+ }
+
protected ActivityAndWindowManagersState mAmWmState = new ActivityAndWindowManagersState();
@Override
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/WindowManagerState.java b/hostsidetests/services/activitymanager/src/android/server/cts/WindowManagerState.java
index 097b659..169e245 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/WindowManagerState.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/WindowManagerState.java
@@ -34,14 +34,15 @@
import static com.android.ddmlib.Log.LogLevel.INFO;
class WindowManagerState {
+ private static final String DUMPSYS_WINDOWS_APPS = "dumpsys window apps";
private static final String DUMPSYS_WINDOWS_VISIBLE_APPS = "dumpsys window visible-apps";
private final Pattern mWindowPattern =
- Pattern.compile("Window #(\\d+) Window\\{(.+) u(\\d+) (.+)\\}\\:");
+ Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) (.+)\\}\\:");
private final Pattern mStartingWindowPattern =
- Pattern.compile("Window #(\\d+) Window\\{(.+) u(\\d+) Starting (.+)\\}\\:");
+ Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) Starting (.+)\\}\\:");
private final Pattern mExitingWindowPattern =
- Pattern.compile("Window #(\\d+) Window\\{(.+) u(\\d+) (.+) EXITING\\}\\:");
+ Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) (.+) EXITING\\}\\:");
private final Pattern mFocusedWindowPattern =
Pattern.compile("mCurrentFocus=Window\\{(.+) u(\\d+) (\\S+)\\}");
@@ -57,12 +58,13 @@
// Windows in z-order with the top most at the front of the list.
private List<String> mWindows = new ArrayList();
+ private List<String> mRawWindows = new ArrayList();
private List<WindowStack> mStacks = new ArrayList();
private String mFocusedWindow = null;
private String mFocusedApp = null;
private final LinkedList<String> mSysDump = new LinkedList();
- void computeState(ITestDevice device) throws DeviceNotAvailableException {
+ void computeState(ITestDevice device, boolean visibleOnly) throws DeviceNotAvailableException {
// It is possible the system is in the middle of transition to the right state when we get
// the dump. We try a few times to get the information we need before giving up.
int retriesLeft = 3;
@@ -82,9 +84,11 @@
}
final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- device.executeShellCommand(DUMPSYS_WINDOWS_VISIBLE_APPS, outputReceiver);
+ final String dumpsysCmd = visibleOnly ?
+ DUMPSYS_WINDOWS_VISIBLE_APPS : DUMPSYS_WINDOWS_APPS;
+ device.executeShellCommand(dumpsysCmd, outputReceiver);
dump = outputReceiver.getOutput();
- parseSysDump(dump);
+ parseSysDump(dump, visibleOnly);
retry = mWindows.isEmpty() || mFocusedWindow == null || mFocusedApp == null;
} while (retry && retriesLeft-- > 0);
@@ -104,7 +108,7 @@
}
}
- private void parseSysDump(String sysDump) {
+ private void parseSysDump(String sysDump,boolean visibleOnly) {
reset();
Collections.addAll(mSysDump, sysDump.split("\\n"));
@@ -125,7 +129,7 @@
CLog.logAndDisplay(INFO, line);
final String window = matcher.group(4);
- if (mWindows.isEmpty()) {
+ if (visibleOnly && mWindows.isEmpty()) {
// This is the front window. Check to see if we are in the middle of
// transitioning. If we are, we want to skip dumping until window manager is
// done transitioning the top window.
@@ -146,6 +150,7 @@
CLog.logAndDisplay(INFO, window);
mWindows.add(window);
+ mRawWindows.add(line);
continue;
}
@@ -169,6 +174,20 @@
}
}
+ void getMatchingWindowTokens(final String windowName, List<String> tokenList) {
+ tokenList.clear();
+
+ for (String line : mRawWindows) {
+ if (line.contains(windowName)) {
+ Matcher matcher = mWindowPattern.matcher(line);
+ if (matcher.matches()) {
+ CLog.logAndDisplay(INFO, "Found activity window: " + line);
+ tokenList.add(matcher.group(2));
+ }
+ }
+ }
+ }
+
String getFrontWindow() {
return mWindows.get(0);
}
@@ -195,8 +214,10 @@
}
private void reset() {
+ mSysDump.clear();
mStacks.clear();
mWindows.clear();
+ mRawWindows.clear();
mFocusedWindow = null;
mFocusedApp = null;
}