Home | History | Annotate | Download | only in hostside
      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