1 /* 2 * Copyright (C) 2023 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.host.systemui; 18 19 import static android.host.systemui.TileServiceTest.START_ACTIVITY_AND_COLLAPSE; 20 21 import android.compat.cts.CompatChangeGatingTestCase; 22 23 import com.android.compatibility.common.util.ApiTest; 24 import com.android.tradefed.device.DeviceNotAvailableException; 25 import com.android.tradefed.util.RunUtil; 26 27 import java.util.Set; 28 29 public class CompatChangesTileServiceTest extends CompatChangeGatingTestCase { 30 31 // Constants for generating commands below. 32 private static final String PACKAGE = "android.systemui.cts"; 33 34 // Commands used on the device. 35 private static final String ADD_TILE = "cmd statusbar add-tile "; 36 private static final String REM_TILE = "cmd statusbar remove-tile "; 37 private static final String OPEN_SETTINGS = "cmd statusbar expand-settings"; 38 39 public static final String REQUEST_SUPPORTED = "cmd statusbar check-support"; 40 public static final String TEST_PREFIX = "TileTest_"; 41 42 // Time between checks for logs we expect. 43 private static final long CHECK_DELAY = 500; 44 // Number of times to check before failing. 45 private static final long CHECK_RETRIES = 30; 46 47 private final String mService = "TestTileService"; 48 private final String mComponent = PACKAGE + "/." + mService; 49 50 private static final long START_ACTIVITY_NEEDS_PENDING_INTENT = 241766793L; 51 52 @Override setUp()53 protected void setUp() throws Exception { 54 super.setUp(); 55 56 clearLogcat(); 57 } 58 59 @Override tearDown()60 protected void tearDown() throws Exception { 61 super.tearDown(); 62 63 remTile(); 64 // Try to wait for a onTileRemoved. 65 waitFor("onTileRemoved"); 66 67 resetCompatChanges(Set.of(START_ACTIVITY_NEEDS_PENDING_INTENT), PACKAGE); 68 RunUtil.getDefault().sleep(100); 69 } 70 71 @ApiTest(apis = {"android.service.quicksettings.TileService#startActivityAndCollapse(Intent)"}) testStartActivityWithIntent_requiresPendingIntent_ThrowsException()72 public void testStartActivityWithIntent_requiresPendingIntent_ThrowsException() 73 throws Exception { 74 if (!supported()) return; 75 Set<Long> enabledSet = Set.of(START_ACTIVITY_NEEDS_PENDING_INTENT); 76 Set<Long> disabledSet = Set.of(); 77 78 setCompatConfig(enabledSet, disabledSet, PACKAGE); 79 addTile(); 80 // Wait for the tile to be added. 81 assertTrue(waitFor("onTileAdded")); 82 83 // Open the quick settings and make sure the tile gets a chance to listen. 84 openSettings(); 85 assertTrue(waitFor("onStartListening")); 86 87 // Trigger the startActivityAndCollapse call and verify calling the method throws 88 // an UnsupportedOperationException. 89 getDevice().executeShellCommand(START_ACTIVITY_AND_COLLAPSE); 90 assertTrue(waitFor("handleStartActivity")); 91 assertTrue(waitFor("UnsupportedOperationException")); 92 93 // Verify that the activity is not launched 94 assertFalse((waitFor("TestActivity#onResume"))); 95 } 96 97 @ApiTest(apis = {"android.service.quicksettings.TileService#startActivityAndCollapse(Intent)"}) testStartActivityWithIntent_doesNotRequirePendingIntent_lauchesSuccessfully()98 public void testStartActivityWithIntent_doesNotRequirePendingIntent_lauchesSuccessfully() 99 throws Exception { 100 if (!supported()) return; 101 Set<Long> enabledSet = Set.of(); 102 Set<Long> disabledSet = Set.of(START_ACTIVITY_NEEDS_PENDING_INTENT); 103 104 setCompatConfig(enabledSet, disabledSet, PACKAGE); 105 addTile(); 106 // Wait for the tile to be added. 107 assertTrue(waitFor("onTileAdded")); 108 109 // Open the quick settings and make sure the tile gets a chance to listen. 110 openSettings(); 111 assertTrue(waitFor("onStartListening")); 112 113 // Trigger the startActivityAndCollapse call and verify calling the method does not throw 114 // an UnsupportedOperationException. 115 getDevice().executeShellCommand(START_ACTIVITY_AND_COLLAPSE); 116 assertTrue(waitFor("handleStartActivity")); 117 118 // Verify that the activity is launched 119 assertTrue((waitFor("TestActivity#onResume"))); 120 } 121 addTile()122 private void addTile() throws Exception { 123 execute(ADD_TILE + mComponent); 124 } 125 remTile()126 private void remTile() throws Exception { 127 execute(REM_TILE + mComponent); 128 } 129 openSettings()130 protected void openSettings() throws Exception { 131 execute(OPEN_SETTINGS); 132 } 133 execute(String cmd)134 private void execute(String cmd) throws Exception { 135 getDevice().executeShellCommand(cmd); 136 // All of the status bar commands tend to have animations associated 137 // everything seems to be happier if you give them time to finish. 138 RunUtil.getDefault().sleep(100); 139 } 140 waitFor(String str)141 protected boolean waitFor(String str) throws DeviceNotAvailableException, InterruptedException { 142 final String searchStr = TEST_PREFIX + str; 143 int ct = 0; 144 while (!hasLog(searchStr) && (ct++ < CHECK_RETRIES)) { 145 RunUtil.getDefault().sleep(CHECK_DELAY); 146 } 147 return hasLog(searchStr); 148 } 149 hasLog(String str)150 protected boolean hasLog(String str) throws DeviceNotAvailableException { 151 String logs = getDevice().executeAdbCommand("logcat", "-v", "brief", "-d", mService + ":I", 152 "*:S"); 153 return logs.contains(str); 154 } 155 clearLogcat()156 private void clearLogcat() throws DeviceNotAvailableException { 157 getDevice().executeAdbCommand("logcat", "-c"); 158 } 159 supported()160 protected boolean supported() throws DeviceNotAvailableException { 161 return supportedHardware() && supportedSoftware(); 162 } 163 supportedSoftware()164 private boolean supportedSoftware() throws DeviceNotAvailableException { 165 String supported = getDevice().executeShellCommand(REQUEST_SUPPORTED); 166 return Boolean.parseBoolean(supported.trim()); 167 } 168 supportedHardware()169 private boolean supportedHardware() throws DeviceNotAvailableException { 170 String features = getDevice().executeShellCommand("pm list features"); 171 return !features.contains("android.hardware.type.television") 172 && !features.contains("android.hardware.type.watch"); 173 } 174 } 175