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 com.android.cts.numberblocking.hostside; 18 19 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 20 import com.android.ddmlib.Log; 21 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; 22 import com.android.tradefed.build.IBuildInfo; 23 import com.android.tradefed.device.DeviceNotAvailableException; 24 import com.android.tradefed.log.LogUtil; 25 import com.android.tradefed.result.CollectingTestListener; 26 import com.android.tradefed.testtype.DeviceTestCase; 27 import com.android.tradefed.testtype.IBuildReceiver; 28 import com.android.tradefed.util.RunInterruptedException; 29 import com.android.tradefed.util.RunUtil; 30 31 import java.io.File; 32 import java.util.regex.Matcher; 33 import java.util.regex.Pattern; 34 35 /** 36 * Multi-user tests for number blocking. 37 */ 38 // To run the tests in this file w/o running all the cts tests: 39 // make cts 40 // cts-tradefed 41 // run cts -m CtsHostsideNumberBlockingTestCases 42 public class NumberBlockingTest extends DeviceTestCase implements IBuildReceiver { 43 private static final String BLOCKED_NUMBER = "556"; 44 private static final String PHONE_ACCOUNT_ID = "test_call_provider_id"; 45 private static final String TEST_APK = "CtsHostsideNumberBlockingAppTest.apk"; 46 private static final String NUMBER_BLOCKING_TESTS_PKG = 47 NumberBlockingTest.class.getPackage().getName(); 48 private static final String CALL_BLOCKING_TEST_CLASS_NAME = "CallBlockingTest"; 49 private static final String NUMBER_BLOCKING_APP_TEST_CLASS_NAME = "NumberBlockingAppTest"; 50 private static final String TEST_APP_CONNECTION_SERVICE_NAME = "DummyConnectionService"; 51 private static final String SECONDARY_USER_NAME = "NumberBlockingTest SecondaryUser"; 52 private static final String FEATURE_TELEPHONY = "android.hardware.telephony"; 53 private static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice"; 54 55 private int mSecondaryUserId; 56 private int mPrimaryUserSerialNumber; 57 private int mSecondaryUserSerialNumber; 58 59 private IBuildInfo mCtsBuild; 60 private boolean mHasFeature; 61 62 @Override setUp()63 protected void setUp() throws Exception { 64 super.setUp(); 65 66 mHasFeature = getDevice().isMultiUserSupported() 67 && getDevice().hasFeature(FEATURE_TELEPHONY) 68 && getDevice().hasFeature(FEATURE_CONNECTION_SERVICE); 69 70 if (!mHasFeature) { 71 return; 72 } 73 74 installTestAppForUser(getDevice().getPrimaryUserId()); 75 createSecondaryUser(); 76 installTestAppForUser(mSecondaryUserId); 77 78 mPrimaryUserSerialNumber = getUserSerialNumber(getDevice().getPrimaryUserId()); 79 mSecondaryUserSerialNumber = getUserSerialNumber(mSecondaryUserId); 80 } 81 82 @Override tearDown()83 protected void tearDown() throws Exception { 84 if (mHasFeature) { 85 getDevice().removeUser(mSecondaryUserId); 86 } 87 88 super.tearDown(); 89 } 90 91 @Override setBuild(IBuildInfo iBuildInfo)92 public void setBuild(IBuildInfo iBuildInfo) { 93 mCtsBuild = iBuildInfo; 94 } 95 testNumberBlocking()96 public void testNumberBlocking() throws Exception { 97 if (!mHasFeature) { 98 LogUtil.CLog.logAndDisplay(Log.LogLevel.INFO, 99 "Skipping number blocking test as the feature is not supported."); 100 return; 101 } 102 103 try { 104 // First run tests for primary user. 105 // Cleanup state prior to running tests. 106 setTestAppAsDefaultSmsAppForUser( 107 true /* setToSmsApp */, getDevice().getPrimaryUserId()); 108 runTestAsPrimaryUser(NUMBER_BLOCKING_APP_TEST_CLASS_NAME, 109 "testCleanupBlockedNumberAsPrimaryUserSucceeds"); 110 111 // Block a number as a privileged app that can block numbers. 112 runTestAsPrimaryUser( 113 NUMBER_BLOCKING_APP_TEST_CLASS_NAME, "testBlockNumberAsPrimaryUserSucceeds"); 114 setTestAppAsDefaultSmsAppForUser( 115 false /* setToSmsApp */, getDevice().getPrimaryUserId()); 116 117 // Ensure incoming call from blocked number is rejected, and unregister the phone 118 // account. 119 runTestAsPrimaryUser(CALL_BLOCKING_TEST_CLASS_NAME, "testRegisterPhoneAccount"); 120 enablePhoneAccountForUser(mPrimaryUserSerialNumber); 121 runTestAsPrimaryUser(CALL_BLOCKING_TEST_CLASS_NAME, 122 "testIncomingCallFromBlockedNumberIsRejected"); 123 runTestAsPrimaryUser(CALL_BLOCKING_TEST_CLASS_NAME, "testUnregisterPhoneAccount"); 124 125 // Run tests as secondary user. 126 startUserAndWait(mSecondaryUserId); 127 128 // Ensure that a privileged app cannot block numbers when the current user is a 129 // secondary user. 130 setTestAppAsDefaultSmsAppForUser(true /* setToSmsApp */, mSecondaryUserId); 131 runTestAsSecondaryUser(NUMBER_BLOCKING_APP_TEST_CLASS_NAME, 132 "testSecondaryUserCannotBlockNumbers"); 133 setTestAppAsDefaultSmsAppForUser(false /* setToSmsApp */, mSecondaryUserId); 134 135 // Calls should be blocked by Telecom for secondary users as well. 136 runTestAsSecondaryUser(CALL_BLOCKING_TEST_CLASS_NAME, "testRegisterPhoneAccount"); 137 enablePhoneAccountForUser(mSecondaryUserSerialNumber); 138 runTestAsSecondaryUser(CALL_BLOCKING_TEST_CLASS_NAME, 139 "testIncomingCallFromBlockedNumberIsRejected"); 140 } finally { 141 // Cleanup state by unblocking the blocked number. 142 setTestAppAsDefaultSmsAppForUser( 143 true /* setToSmsApp */, getDevice().getPrimaryUserId()); 144 runTestAsPrimaryUser( 145 NUMBER_BLOCKING_APP_TEST_CLASS_NAME, "testUnblockNumberAsPrimaryUserSucceeds"); 146 } 147 } 148 149 /** Starts user {@code userId} and waits until it is in state RUNNING_UNLOCKED. */ startUserAndWait(int userId)150 protected void startUserAndWait(int userId) throws Exception { 151 getDevice().startUser(userId); 152 153 final String desiredState = "RUNNING_UNLOCKED"; 154 final long USER_STATE_TIMEOUT_MS = 60_0000; // 1 minute 155 final long timeout = System.currentTimeMillis() + USER_STATE_TIMEOUT_MS; 156 final String command = String.format("am get-started-user-state %d", userId); 157 String output = ""; 158 while (System.currentTimeMillis() <= timeout) { 159 output = getDevice().executeShellCommand(command); 160 if (output.contains(desiredState)) { 161 return; 162 } 163 RunUtil.getDefault().sleep(100); 164 } 165 fail("User state of " + userId + " was '" + output + "' rather than " + desiredState); 166 } 167 createSecondaryUser()168 private void createSecondaryUser() throws Exception { 169 mSecondaryUserId = getDevice().createUser(SECONDARY_USER_NAME); 170 getDevice().waitForDeviceAvailable(); 171 } 172 installTestAppForUser(int userId)173 private void installTestAppForUser(int userId) throws Exception { 174 LogUtil.CLog.logAndDisplay(Log.LogLevel.INFO, "Installing test app for user: " + userId); 175 CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild); 176 File testAppFile = buildHelper.getTestFile(TEST_APK); 177 String installResult; 178 try { 179 installResult = getDevice().installPackageForUser( 180 testAppFile, true /*reinstall*/, userId); 181 } catch (DeviceNotAvailableException dna) { 182 fail("Device not available to install test app " + dna); 183 return; 184 } 185 assertNull(String.format( 186 "failed to install number blocking test app. Reason: %s", installResult), 187 installResult); 188 189 waitForTestAppInstallation(NUMBER_BLOCKING_TESTS_PKG); 190 } 191 runTestAsPrimaryUser(String className, String methodName)192 private void runTestAsPrimaryUser(String className, String methodName) throws Exception { 193 runTestAsUser(className, methodName, getDevice().getPrimaryUserId()); 194 } 195 runTestAsSecondaryUser(String className, String methodName)196 private void runTestAsSecondaryUser(String className, String methodName) throws Exception { 197 runTestAsUser(className, methodName, mSecondaryUserId); 198 } 199 runTestAsUser(String className, String methodName, int userId)200 private void runTestAsUser(String className, String methodName, int userId) throws Exception { 201 LogUtil.CLog.logAndDisplay(Log.LogLevel.INFO, "Running %s.%s for user: %d", 202 className, methodName, userId); 203 RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner( 204 NUMBER_BLOCKING_TESTS_PKG, 205 "androidx.test.runner.AndroidJUnitRunner", 206 getDevice().getIDevice()); 207 testRunner.addInstrumentationArg("blocked_number", BLOCKED_NUMBER); 208 testRunner.addInstrumentationArg("phone_account_id", PHONE_ACCOUNT_ID); 209 testRunner.setMethodName(NUMBER_BLOCKING_TESTS_PKG + "." + className, methodName); 210 CollectingTestListener listener = new CollectingTestListener(); 211 getDevice().runInstrumentationTestsAsUser(testRunner, userId, listener); 212 assertEquals(1, listener.getNumTotalTests()); 213 assertFalse(listener.getCurrentRunResults().getTestResults().keySet().toString(), 214 listener.getCurrentRunResults().hasFailedTests()); 215 } 216 enablePhoneAccountForUser(int userSerialNumber)217 private void enablePhoneAccountForUser(int userSerialNumber) throws Exception { 218 String command = String.format( 219 "telecom set-phone-account-enabled %s\\/%s.%s\\$%s %s %d", 220 NUMBER_BLOCKING_TESTS_PKG, 221 NUMBER_BLOCKING_TESTS_PKG, 222 CALL_BLOCKING_TEST_CLASS_NAME, 223 TEST_APP_CONNECTION_SERVICE_NAME, 224 PHONE_ACCOUNT_ID, 225 userSerialNumber); 226 String commandResponse = getDevice().executeShellCommand(command); 227 assertTrue(commandResponse, commandResponse.contains("Success")); 228 } 229 setTestAppAsDefaultSmsAppForUser(boolean setToSmsApp, int userId)230 private void setTestAppAsDefaultSmsAppForUser(boolean setToSmsApp, int userId) 231 throws Exception { 232 String command = String.format("appops set --user %d %s WRITE_SMS %s", userId, 233 NUMBER_BLOCKING_TESTS_PKG, 234 setToSmsApp ? "allow" : "default"); 235 assertEquals("", getDevice().executeShellCommand(command)); 236 } 237 238 // TODO: Replace this with API in ITestDevice once it is available. getUserSerialNumber(int userId)239 private int getUserSerialNumber(int userId) throws DeviceNotAvailableException { 240 Pattern pattern = Pattern.compile("^.*UserInfo\\{" + userId + "\\:.*serialNo=(\\d+).*$"); 241 // dumpsys user return lines like "UserInfo{0:Owner:13} serialNo=0" 242 String commandOutput = getDevice().executeShellCommand("dumpsys user"); 243 String[] tokens = commandOutput.split("\\n"); 244 for (String token : tokens) { 245 token = token.trim(); 246 Matcher matcher = pattern.matcher(token); 247 if (matcher.matches()) { 248 int serialNumber = Integer.parseInt(matcher.group(1)); 249 LogUtil.CLog.logAndDisplay( 250 Log.LogLevel.INFO, 251 String.format("Serial number of user %d : %d", userId, serialNumber)); 252 return serialNumber; 253 } 254 } 255 fail("Couldn't find user " + userId); 256 return -1; 257 } 258 waitForTestAppInstallation(String packageName)259 private void waitForTestAppInstallation(String packageName) { 260 try { 261 int retries = 0; 262 while (!getDevice().getInstalledPackageNames().contains(packageName) 263 && retries < 10) { 264 RunUtil.getDefault().sleep(50); 265 retries++; 266 } 267 268 assertTrue(getDevice().getInstalledPackageNames().contains(packageName)); 269 } catch (DeviceNotAvailableException dne) { 270 fail("Device not available."); 271 } catch (RunInterruptedException ie) { 272 fail("Failed to wait for change."); 273 } 274 } 275 } 276