1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.server.wm; 18 19 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; 20 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 21 import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; 22 import static android.server.wm.ComponentNameUtils.getActivityName; 23 import static android.server.wm.UiDeviceUtils.pressHomeButton; 24 import static android.server.wm.app.Components.ENTRY_POINT_ALIAS_ACTIVITY; 25 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY; 26 import static android.server.wm.app.Components.SINGLE_TASK_ACTIVITY; 27 import static android.server.wm.app.Components.TEST_ACTIVITY; 28 import static android.view.Display.DEFAULT_DISPLAY; 29 30 import static org.hamcrest.MatcherAssert.assertThat; 31 import static org.hamcrest.Matchers.greaterThanOrEqualTo; 32 import static org.junit.Assert.assertEquals; 33 import static org.junit.Assert.assertNotEquals; 34 import static org.junit.Assert.fail; 35 36 import android.content.ComponentName; 37 import android.platform.test.annotations.Presubmit; 38 39 import androidx.test.filters.FlakyTest; 40 41 import org.junit.Test; 42 43 import java.util.regex.Matcher; 44 import java.util.regex.Pattern; 45 46 /** 47 * Build/Install/Run: 48 * atest CtsWindowManagerDeviceTestCases:AmStartOptionsTests 49 */ 50 @Presubmit 51 public class AmStartOptionsTests extends ActivityManagerTestBase { 52 53 @Test testDashD()54 public void testDashD() { 55 // Run at least 2 rounds to verify that -D works with an existing process. 56 // -D could fail in this case if the force stop of process is broken. 57 int prevProcId = -1; 58 for (int i = 0; i < 2; i++) { 59 executeShellCommand("am start -n " + getActivityName(TEST_ACTIVITY) + " -D"); 60 61 mAmWmState.waitForDebuggerWindowVisible(TEST_ACTIVITY); 62 int procId = mAmWmState.getAmState().getActivityProcId(TEST_ACTIVITY); 63 64 assertThat("Invalid ProcId.", procId, greaterThanOrEqualTo(0)); 65 if (i > 0) { 66 assertNotEquals("Run " + i + " didn't start new proc.", prevProcId, procId); 67 } 68 prevProcId = procId; 69 } 70 } 71 72 @Test testDashW_Direct()73 public void testDashW_Direct() throws Exception { 74 testDashW(SINGLE_TASK_ACTIVITY, SINGLE_TASK_ACTIVITY); 75 } 76 77 @Test 78 @FlakyTest testDashW_Indirect()79 public void testDashW_Indirect() throws Exception { 80 testDashW(ENTRY_POINT_ALIAS_ACTIVITY, SINGLE_TASK_ACTIVITY); 81 } 82 83 @Test testDashW_FinishingTop()84 public void testDashW_FinishingTop() { 85 // Start LaunchingActivity and TestActivity 86 getLaunchActivityBuilder().setLaunchingActivity(LAUNCHING_ACTIVITY) 87 .setTargetActivity(TEST_ACTIVITY).execute(); 88 89 // Return to home 90 pressHomeButton(); 91 92 // Start LaunchingActivity again and finish TestActivity 93 final int flags = 94 FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP; 95 final String result = executeShellCommand( 96 "am start -W -f " + flags + " -n " + getActivityName(LAUNCHING_ACTIVITY)); 97 verifyShellOutput(result, LAUNCHING_ACTIVITY, false); 98 } 99 testDashW(final ComponentName entryActivity, final ComponentName actualActivity)100 private void testDashW(final ComponentName entryActivity, final ComponentName actualActivity) 101 throws Exception { 102 // Test cold start 103 startActivityAndVerifyResult(entryActivity, actualActivity, true); 104 105 // Test warm start 106 pressHomeButton(); 107 startActivityAndVerifyResult(entryActivity, actualActivity, false); 108 109 // Test "hot" start (app already in front) 110 startActivityAndVerifyResult(entryActivity, actualActivity, false); 111 } 112 startActivityAndVerifyResult(final ComponentName entryActivity, final ComponentName actualActivity, boolean shouldStart)113 private void startActivityAndVerifyResult(final ComponentName entryActivity, 114 final ComponentName actualActivity, boolean shouldStart) { 115 // See TODO below 116 // final LogSeparator logSeparator = separateLogs(); 117 118 // Pass in different data only when cold starting. This is to make the intent 119 // different in subsequent warm/hot launches, so that the entrypoint alias 120 // activity is always started, but the actual activity is not started again 121 // because of the NEW_TASK and singleTask flags. 122 final String result = executeShellCommand( 123 "am start -n " + getActivityName(entryActivity) + " -W" 124 + (shouldStart ? " -d about:blank" : "")); 125 126 // Verify shell command return value 127 verifyShellOutput(result, actualActivity, shouldStart); 128 129 // TODO: Disable logcat check for now. 130 // Logcat of WM or AM tag could be lost (eg. chatty if earlier events generated 131 // too many lines), and make the test look flaky. We need to either use event 132 // log or swith to other mechanisms. Only verify shell output for now, it should 133 // still catch most failures. 134 135 // Verify adb logcat log 136 //verifyLogcat(actualActivity, shouldStart, logSeparator); 137 } 138 139 private static final Pattern sNotStartedWarningPattern = Pattern.compile( 140 "Warning: Activity not started(.*)"); 141 private static final Pattern sStatusPattern = Pattern.compile( 142 "Status: (.*)"); 143 private static final Pattern sActivityPattern = Pattern.compile( 144 "Activity: (.*)"); 145 private static final String sStatusOk = "ok"; 146 verifyShellOutput( final String result, final ComponentName activity, boolean shouldStart)147 private void verifyShellOutput( 148 final String result, final ComponentName activity, boolean shouldStart) { 149 boolean warningFound = false; 150 String status = null; 151 String reportedActivity = null; 152 153 final String[] lines = result.split("\\n"); 154 // Going from the end of logs to beginning in case if some other activity is started first. 155 for (int i = lines.length - 1; i >= 0; i--) { 156 final String line = lines[i].trim(); 157 Matcher matcher = sNotStartedWarningPattern.matcher(line); 158 if (matcher.matches()) { 159 warningFound = true; 160 continue; 161 } 162 matcher = sStatusPattern.matcher(line); 163 if (matcher.matches()) { 164 status = matcher.group(1); 165 continue; 166 } 167 matcher = sActivityPattern.matcher(line); 168 if (matcher.matches()) { 169 reportedActivity = matcher.group(1); 170 continue; 171 } 172 } 173 174 assertEquals("Status is ok", sStatusOk, status); 175 assertEquals("Reported activity is " + getActivityName(activity), 176 getActivityName(activity), reportedActivity); 177 178 if (shouldStart && warningFound) { 179 fail("Should start new activity but brought something to front."); 180 } else if (!shouldStart && !warningFound){ 181 fail("Should bring existing activity to front but started new activity."); 182 } 183 } 184 } 185