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 29 import java.io.File; 30 31 /** 32 * Multi-user tests for number blocking. 33 */ 34 // To run the tests in this file w/o running all the cts tests: 35 // make cts 36 // cts-tradefed 37 // run cts -m CtsHostsideNumberBlockingTestCases 38 public class NumberBlockingTest extends DeviceTestCase implements IBuildReceiver { 39 private static final String BLOCKED_NUMBER = "556"; 40 private static final String PHONE_ACCOUNT_ID = "test_call_provider_id"; 41 private static final String TEST_APK = "CtsHostsideNumberBlockingAppTest.apk"; 42 private static final String NUMBER_BLOCKING_TESTS_PKG = 43 NumberBlockingTest.class.getPackage().getName(); 44 private static final String CALL_BLOCKING_TEST_CLASS_NAME = "CallBlockingTest"; 45 private static final String NUMBER_BLOCKING_APP_TEST_CLASS_NAME = "NumberBlockingAppTest"; 46 private static final String TEST_APP_CONNECTION_SERVICE_NAME = "DummyConnectionService"; 47 private static final String SECONDARY_USER_NAME = "NumberBlockingTest SecondaryUser"; 48 private static final String FEATURE_TELEPHONY = "android.hardware.telephony"; 49 private static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice"; 50 51 private int mSecondaryUserId; 52 private int mPrimaryUserSerialNumber; 53 private int mSecondaryUserSerialNumber; 54 55 private IBuildInfo mCtsBuild; 56 private boolean mHasFeature; 57 58 @Override 59 protected void setUp() throws Exception { 60 super.setUp(); 61 62 mHasFeature = getDevice().isMultiUserSupported() 63 && getDevice().hasFeature(FEATURE_TELEPHONY) 64 && getDevice().hasFeature(FEATURE_CONNECTION_SERVICE); 65 66 if (!mHasFeature) { 67 return; 68 } 69 70 installTestAppForUser(getDevice().getPrimaryUserId()); 71 createSecondaryUser(); 72 installTestAppForUser(mSecondaryUserId); 73 74 mPrimaryUserSerialNumber = getUserSerialNumber(getDevice().getPrimaryUserId()); 75 mSecondaryUserSerialNumber = getUserSerialNumber(mSecondaryUserId); 76 } 77 78 @Override 79 protected void tearDown() throws Exception { 80 if (mHasFeature) { 81 getDevice().removeUser(mSecondaryUserId); 82 } 83 84 super.tearDown(); 85 } 86 87 @Override 88 public void setBuild(IBuildInfo iBuildInfo) { 89 mCtsBuild = iBuildInfo; 90 } 91 92 public void testNumberBlocking() throws Exception { 93 if (!mHasFeature) { 94 LogUtil.CLog.logAndDisplay(Log.LogLevel.INFO, 95 "Skipping number blocking test as the feature is not supported."); 96 return; 97 } 98 99 try { 100 // First run tests for primary user. 101 // Cleanup state prior to running tests. 102 setTestAppAsDefaultSmsAppForUser( 103 true /* setToSmsApp */, getDevice().getPrimaryUserId()); 104 runTestAsPrimaryUser(NUMBER_BLOCKING_APP_TEST_CLASS_NAME, 105 "testCleanupBlockedNumberAsPrimaryUserSucceeds"); 106 107 // Block a number as a privileged app that can block numbers. 108 runTestAsPrimaryUser( 109 NUMBER_BLOCKING_APP_TEST_CLASS_NAME, "testBlockNumberAsPrimaryUserSucceeds"); 110 setTestAppAsDefaultSmsAppForUser( 111 false /* setToSmsApp */, getDevice().getPrimaryUserId()); 112 113 // Ensure incoming call from blocked number is rejected, and unregister the phone 114 // account. 115 runTestAsPrimaryUser(CALL_BLOCKING_TEST_CLASS_NAME, "testRegisterPhoneAccount"); 116 enablePhoneAccountForUser(mPrimaryUserSerialNumber); 117 runTestAsPrimaryUser(CALL_BLOCKING_TEST_CLASS_NAME, 118 "testIncomingCallFromBlockedNumberIsRejected"); 119 runTestAsPrimaryUser(CALL_BLOCKING_TEST_CLASS_NAME, "testUnregisterPhoneAccount"); 120 121 // Run tests as secondary user. 122 startUserAndWait(mSecondaryUserId); 123 124 // Ensure that a privileged app cannot block numbers when the current user is a 125 // secondary user. 126 setTestAppAsDefaultSmsAppForUser(true /* setToSmsApp */, mSecondaryUserId); 127 runTestAsSecondaryUser(NUMBER_BLOCKING_APP_TEST_CLASS_NAME, 128 "testSecondaryUserCannotBlockNumbers"); 129 setTestAppAsDefaultSmsAppForUser(false /* setToSmsApp */, mSecondaryUserId); 130 131 // Calls should be blocked by Telecom for secondary users as well. 132 runTestAsSecondaryUser(CALL_BLOCKING_TEST_CLASS_NAME, "testRegisterPhoneAccount"); 133 enablePhoneAccountForUser(mSecondaryUserSerialNumber); 134 runTestAsSecondaryUser(CALL_BLOCKING_TEST_CLASS_NAME, 135 "testIncomingCallFromBlockedNumberIsRejected"); 136 } finally { 137 // Cleanup state by unblocking the blocked number. 138 setTestAppAsDefaultSmsAppForUser( 139 true /* setToSmsApp */, getDevice().getPrimaryUserId()); 140 runTestAsPrimaryUser( 141 NUMBER_BLOCKING_APP_TEST_CLASS_NAME, "testUnblockNumberAsPrimaryUserSucceeds"); 142 } 143 } 144 145 /** Starts user {@code userId} and waits until it is in state RUNNING_UNLOCKED. */ 146 protected void startUserAndWait(int userId) throws Exception { 147 getDevice().startUser(userId); 148 149 final String desiredState = "RUNNING_UNLOCKED"; 150 final long USER_STATE_TIMEOUT_MS = 60_0000; // 1 minute 151 final long timeout = System.currentTimeMillis() + USER_STATE_TIMEOUT_MS; 152 final String command = String.format("am get-started-user-state %d", userId); 153 String output = ""; 154 while (System.currentTimeMillis() <= timeout) { 155 output = getDevice().executeShellCommand(command); 156 if (output.contains(desiredState)) { 157 return; 158 } 159 try { 160 Thread.sleep(100); 161 } catch (InterruptedException e) { 162 // Do nothing. 163 } 164 } 165 fail("User state of " + userId + " was '" + output + "' rather than " + desiredState); 166 } 167 168 private void createSecondaryUser() throws Exception { 169 mSecondaryUserId = getDevice().createUser(SECONDARY_USER_NAME); 170 getDevice().waitForDeviceAvailable(); 171 } 172 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 192 private void runTestAsPrimaryUser(String className, String methodName) throws Exception { 193 runTestAsUser(className, methodName, getDevice().getPrimaryUserId()); 194 } 195 196 private void runTestAsSecondaryUser(String className, String methodName) throws Exception { 197 runTestAsUser(className, methodName, mSecondaryUserId); 198 } 199 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 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 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. 239 private int getUserSerialNumber(int userId) throws DeviceNotAvailableException { 240 // dumpsys user return lines like "UserInfo{0:Owner:13} serialNo=0" 241 String commandOutput = getDevice().executeShellCommand("dumpsys user"); 242 String[] tokens = commandOutput.split("\\n"); 243 for (String token : tokens) { 244 token = token.trim(); 245 if (token.contains("UserInfo{" + userId + ":")) { 246 String[] split = token.split("serialNo="); 247 assertTrue(split.length == 2); 248 int serialNumber = Integer.parseInt(split[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 259 private void waitForTestAppInstallation(String packageName) { 260 try { 261 int retries = 0; 262 while (!getDevice().getInstalledPackageNames().contains(packageName) 263 && retries < 10) { 264 Thread.sleep(50); 265 retries++; 266 } 267 268 assertTrue(getDevice().getInstalledPackageNames().contains(packageName)); 269 } catch (DeviceNotAvailableException dne) { 270 fail("Device not available."); 271 } catch (InterruptedException ie) { 272 fail("Failed to wait for change."); 273 } 274 } 275 } 276