1 /* 2 * Copyright (C) 2019 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 package com.google.android.cts.deviceowner; 17 18 import static android.server.wm.WindowManagerState.STATE_RESUMED; 19 20 import static com.google.common.truth.Truth.assertWithMessage; 21 22 import android.app.admin.DeviceAdminReceiver; 23 import android.app.admin.DevicePolicyManager; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.PackageManager; 28 import android.os.RemoteException; 29 import android.os.UserHandle; 30 import android.provider.Settings; 31 import android.server.wm.WindowManagerStateHelper; 32 import android.test.InstrumentationTestCase; 33 import android.util.Log; 34 35 import androidx.test.InstrumentationRegistry; 36 import androidx.test.uiautomator.By; 37 import androidx.test.uiautomator.UiDevice; 38 import androidx.test.uiautomator.UiObjectNotFoundException; 39 import androidx.test.uiautomator.UiScrollable; 40 import androidx.test.uiautomator.UiSelector; 41 import androidx.test.uiautomator.Until; 42 43 import com.android.bedstead.dpmwrapper.DeviceOwnerHelper; 44 import com.android.bedstead.dpmwrapper.TestAppSystemServiceFactory; 45 import com.android.compatibility.common.util.enterprise.DeviceAdminReceiverUtils; 46 47 /** 48 * Class for device-owner based tests. 49 * 50 * <p>This class handles making sure that the test is the device owner and that it has an active 51 * admin registered if necessary. The admin component can be accessed through {@link #getWho()}. 52 */ 53 public final class DeviceOwnerTest extends InstrumentationTestCase { 54 55 private static final String TAG = DeviceOwnerTest.class.getSimpleName(); 56 57 public static final int TIMEOUT_MS = 2_000; 58 59 public static final double DEADZONE_PCT = 0.2; 60 61 protected Context mContext; 62 protected UiDevice mDevice; 63 64 /** Device Admin receiver for DO. */ 65 public static final class BasicAdminReceiver extends DeviceAdminReceiver { 66 67 @Override onReceive(Context context, Intent intent)68 public void onReceive(Context context, Intent intent) { 69 // Ignore intents used by DpmWrapper IPC between current and system users 70 if (DeviceOwnerHelper.runManagerMethod(this, context, intent)) return; 71 72 // Hack used to manually disable the admin during development 73 if (DeviceAdminReceiverUtils.disableSelf(context, intent)) return; 74 75 super.onReceive(context, intent); 76 } 77 } 78 79 static final String CAR_SETTING_FRAG_RESOURCE_ID_REGEX = ".*:id/fragment_container_wrapper"; 80 static final String PACKAGE_NAME = DeviceOwnerTest.class.getPackage().getName(); 81 static final ComponentName RECEIVER_COMPONENT = 82 new ComponentName(PACKAGE_NAME, BasicAdminReceiver.class.getName()); 83 84 protected DevicePolicyManager mDevicePolicyManager; 85 protected PackageManager mPackageManager; 86 protected boolean mIsDeviceOwner; 87 private String mWorkPolicyInfoText; 88 private boolean mIsAutomotive; 89 90 @Override setUp()91 protected void setUp() throws Exception { 92 super.setUp(); 93 mContext = getInstrumentation().getContext(); 94 mDevice = UiDevice.getInstance(getInstrumentation()); 95 mPackageManager = mContext.getPackageManager(); 96 mDevicePolicyManager = TestAppSystemServiceFactory.getDevicePolicyManager(mContext, 97 BasicAdminReceiver.class, /* forDeviceOwner= */ true); 98 99 mIsAutomotive = mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); 100 101 mWorkPolicyInfoText = mIsAutomotive 102 ? "Privacy Settings for Device Owner CTS host side app vehicle policy" 103 : "Your work policy info"; 104 105 mIsDeviceOwner = mDevicePolicyManager.isDeviceOwnerApp(PACKAGE_NAME); 106 Log.d(TAG, "setup(): dpm=" + mDevicePolicyManager + ", isDO: " + mIsDeviceOwner); 107 108 if (mIsDeviceOwner) { 109 assertWithMessage("isAdminActive(%s)", RECEIVER_COMPONENT) 110 .that(mDevicePolicyManager.isAdminActive(RECEIVER_COMPONENT)).isTrue(); 111 112 // Note DPM.getDeviceOwner() now always returns null on non-DO users as of NYC. 113 assertWithMessage("%s.getDeviceOwner()", mDevicePolicyManager) 114 .that(mDevicePolicyManager.getDeviceOwner()).isEqualTo(PACKAGE_NAME); 115 } 116 117 try { 118 mDevice.setOrientationNatural(); 119 } catch (RemoteException e) { 120 throw new RuntimeException("failed to freeze device orientation", e); 121 } 122 wakeupDeviceAndPressHome(); 123 } 124 wakeupDeviceAndPressHome()125 private void wakeupDeviceAndPressHome() throws Exception { 126 mDevice.wakeUp(); 127 mDevice.pressMenu(); 128 mDevice.pressHome(); 129 } 130 131 @Override tearDown()132 protected void tearDown() throws Exception { 133 mDevice.pressBack(); 134 mDevice.pressHome(); 135 mDevice.waitForIdle(TIMEOUT_MS); // give UI time to finish animating 136 } 137 launchPrivacyAndCheckWorkPolicyInfo()138 private boolean launchPrivacyAndCheckWorkPolicyInfo() throws Exception { 139 // Launch Settings 140 launchSettingsPage(InstrumentationRegistry.getContext(), Settings.ACTION_PRIVACY_SETTINGS); 141 142 // Wait for loading permission usage data. 143 mDevice.waitForIdle(TIMEOUT_MS); 144 145 Log.d(TAG, "Waiting " + TIMEOUT_MS + "ms for the '" + mWorkPolicyInfoText + "' message"); 146 147 boolean found = null != mDevice.wait(Until.findObject(By.text(mWorkPolicyInfoText)), 148 TIMEOUT_MS); 149 150 // For automotive and Safety Center UI, try to scroll the privacy list to find the item 151 if (!found) { 152 UiScrollable scroller; 153 if (mIsAutomotive) { 154 scroller = new UiScrollable(new UiSelector() 155 .resourceIdMatches(CAR_SETTING_FRAG_RESOURCE_ID_REGEX)); 156 } else { 157 scroller = new UiScrollable(new UiSelector().scrollable(true)); 158 } 159 try { 160 // Swipe far away from the edges to avoid triggering navigation gestures 161 scroller.setSwipeDeadZonePercentage(DEADZONE_PCT); 162 found = scroller.scrollTextIntoView(mWorkPolicyInfoText); 163 } catch (UiObjectNotFoundException e) { } 164 } 165 166 Log.d(TAG, "Message found: " + found); 167 return found; 168 } 169 launchSettingsPage(Context ctx, String pageName)170 private void launchSettingsPage(Context ctx, String pageName) throws Exception { 171 Intent intent = new Intent(pageName); 172 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 173 174 Log.d(TAG, "Launching settings page on user " + UserHandle.myUserId() + " using " + intent); 175 ctx.startActivity(intent); 176 177 ComponentName componentName = 178 ctx.getPackageManager() 179 .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) 180 .getComponentInfo() 181 .getComponentName(); 182 183 Log.d(TAG, "Waiting for STATE_RESUMED on " + componentName); 184 185 new WindowManagerStateHelper().waitForActivityState(componentName, STATE_RESUMED); 186 } 187 disableWorkPolicyInfoActivity()188 private void disableWorkPolicyInfoActivity() { 189 mContext.getPackageManager() 190 .setComponentEnabledSetting( 191 new ComponentName(mContext, WorkPolicyInfoActivity.class), 192 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 193 PackageManager.DONT_KILL_APP); 194 } 195 launchPrivacySettingsAndAssertWorkPolicyInfoIsShowing()196 private void launchPrivacySettingsAndAssertWorkPolicyInfoIsShowing() throws Exception { 197 assertWithMessage("Work policy info (%s) on settings entry", mWorkPolicyInfoText) 198 .that(launchPrivacyAndCheckWorkPolicyInfo()).isTrue(); 199 } 200 launchPrivacySettingsAndAssertWorkPolicyInfoIsNotShowing()201 private void launchPrivacySettingsAndAssertWorkPolicyInfoIsNotShowing() throws Exception { 202 assertWithMessage("Work policy info (%s) on settings entry", mWorkPolicyInfoText) 203 .that(launchPrivacyAndCheckWorkPolicyInfo()).isFalse(); 204 } 205 206 /** 207 * If the app is the active device owner and has work policy info, then we should have a Privacy 208 * entry for it. 209 */ testDeviceOwnerWithInfo()210 public void testDeviceOwnerWithInfo() throws Exception { 211 assertWithMessage("is device owner").that(mIsDeviceOwner).isTrue(); 212 213 launchPrivacySettingsAndAssertWorkPolicyInfoIsShowing(); 214 } 215 216 /** 217 * If the app is the active device owner, but doesn't have work policy info, then we shouldn't 218 * have a Privacy entry for it. 219 */ testDeviceOwnerWithoutInfo()220 public void testDeviceOwnerWithoutInfo() throws Exception { 221 assertWithMessage("is device owner").that(mIsDeviceOwner).isTrue(); 222 223 disableWorkPolicyInfoActivity(); 224 225 launchPrivacySettingsAndAssertWorkPolicyInfoIsNotShowing(); 226 } 227 228 /** 229 * If the app is NOT the active device owner, then we should not have a Privacy entry for work 230 * policy info. 231 */ testNonDeviceOwnerWithInfo()232 public void testNonDeviceOwnerWithInfo() throws Exception { 233 assertWithMessage("is device owner").that(mIsDeviceOwner).isFalse(); 234 235 launchPrivacySettingsAndAssertWorkPolicyInfoIsNotShowing(); 236 } 237 238 /** 239 * If the app is NOT the active device owner, and doesn't have work policy info, then we should 240 * not have a Privacy entry for work policy info. 241 */ testNonDeviceOwnerWithoutInfo()242 public void testNonDeviceOwnerWithoutInfo() throws Exception { 243 assertWithMessage("is device owner").that(mIsDeviceOwner).isFalse(); 244 245 disableWorkPolicyInfoActivity(); 246 247 launchPrivacySettingsAndAssertWorkPolicyInfoIsNotShowing(); 248 } 249 } 250