1 /* 2 * Copyright 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.managedprovisioning.provisioning; 18 19 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE; 20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; 21 import static android.support.test.espresso.Espresso.onView; 22 import static android.support.test.espresso.Espresso.pressBack; 23 import static android.support.test.espresso.action.ViewActions.click; 24 import static android.support.test.espresso.assertion.ViewAssertions.matches; 25 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; 26 import static android.support.test.espresso.matcher.ViewMatchers.withId; 27 import static android.support.test.espresso.matcher.ViewMatchers.withText; 28 29 import static com.android.managedprovisioning.common.LogoUtils.saveOrganisationLogo; 30 import static com.android.managedprovisioning.model.CustomizationParams.DEFAULT_COLOR_ID_DO; 31 import static com.android.managedprovisioning.model.CustomizationParams.DEFAULT_COLOR_ID_MP; 32 33 import static org.junit.Assert.assertFalse; 34 import static org.junit.Assert.assertTrue; 35 import static org.mockito.Matchers.any; 36 import static org.mockito.Matchers.anyString; 37 import static org.mockito.Mockito.never; 38 import static org.mockito.Mockito.verify; 39 import static org.mockito.Mockito.verifyNoMoreInteractions; 40 41 import static java.util.Arrays.asList; 42 43 import android.app.Activity; 44 import android.content.ComponentName; 45 import android.content.Context; 46 import android.content.Intent; 47 import android.graphics.Color; 48 import android.os.Bundle; 49 import android.provider.Settings; 50 import android.support.test.InstrumentationRegistry; 51 import android.support.test.filters.SmallTest; 52 import android.support.test.rule.ActivityTestRule; 53 54 import com.android.managedprovisioning.R; 55 import com.android.managedprovisioning.TestInstrumentationRunner; 56 import com.android.managedprovisioning.common.CustomizationVerifier; 57 import com.android.managedprovisioning.common.UriBitmap; 58 import com.android.managedprovisioning.common.Utils; 59 import com.android.managedprovisioning.model.ProvisioningParams; 60 61 import org.junit.After; 62 import org.junit.AfterClass; 63 import org.junit.Before; 64 import org.junit.BeforeClass; 65 import org.junit.Rule; 66 import org.junit.Test; 67 import org.mockito.Mock; 68 import org.mockito.MockitoAnnotations; 69 70 import java.io.IOException; 71 72 /** 73 * Unit tests for {@link ProvisioningActivity}. 74 */ 75 @SmallTest 76 public class ProvisioningActivityTest { 77 private static final String ADMIN_PACKAGE = "com.test.admin"; 78 private static final ComponentName ADMIN = new ComponentName(ADMIN_PACKAGE, ".Receiver"); 79 public static final ProvisioningParams PROFILE_OWNER_PARAMS = new ProvisioningParams.Builder() 80 .setProvisioningAction(ACTION_PROVISION_MANAGED_PROFILE) 81 .setDeviceAdminComponentName(ADMIN) 82 .build(); 83 public static final ProvisioningParams DEVICE_OWNER_PARAMS = new ProvisioningParams.Builder() 84 .setProvisioningAction(ACTION_PROVISION_MANAGED_DEVICE) 85 .setDeviceAdminComponentName(ADMIN) 86 .build(); 87 private static final Intent PROFILE_OWNER_INTENT = new Intent() 88 .putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, PROFILE_OWNER_PARAMS); 89 private static final Intent DEVICE_OWNER_INTENT = new Intent() 90 .putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, DEVICE_OWNER_PARAMS); 91 92 @Rule 93 public ActivityTestRule<ProvisioningActivity> mActivityRule = new ActivityTestRule<>( 94 ProvisioningActivity.class, true /* Initial touch mode */, 95 false /* Lazily launch activity */); 96 97 @Mock private ProvisioningManager mProvisioningManager; 98 @Mock private Utils mUtils; 99 private static int mRotationLocked; 100 101 @BeforeClass setUpClass()102 public static void setUpClass() { 103 // Stop the activity from rotating in order to keep hold of the context 104 Context context = InstrumentationRegistry.getTargetContext(); 105 106 mRotationLocked = Settings.System.getInt(context.getContentResolver(), 107 Settings.System.ACCELEROMETER_ROTATION, 0); 108 Settings.System.putInt(context.getContentResolver(), 109 Settings.System.ACCELEROMETER_ROTATION, 0); 110 } 111 112 @AfterClass tearDownClass()113 public static void tearDownClass() { 114 // Reset the rotation value back to what it was before the test 115 Context context = InstrumentationRegistry.getTargetContext(); 116 117 Settings.System.putInt(context.getContentResolver(), 118 Settings.System.ACCELEROMETER_ROTATION, mRotationLocked); 119 } 120 121 @Before setUp()122 public void setUp() { 123 MockitoAnnotations.initMocks(this); 124 125 TestInstrumentationRunner.registerReplacedActivity(ProvisioningActivity.class, 126 (classLoader, className, intent) -> 127 new ProvisioningActivity(mProvisioningManager, mUtils)); 128 } 129 130 @After tearDown()131 public void tearDown() { 132 TestInstrumentationRunner.unregisterReplacedActivity(ProvisioningActivity.class); 133 } 134 135 @Test testLaunch()136 public void testLaunch() { 137 // GIVEN the activity was launched with a profile owner intent 138 launchActivityAndWait(PROFILE_OWNER_INTENT); 139 140 // THEN the provisioning process should be initiated 141 verify(mProvisioningManager).maybeStartProvisioning(PROFILE_OWNER_PARAMS); 142 143 // THEN the activity should start listening for provisioning updates 144 verify(mProvisioningManager).registerListener(any(ProvisioningManagerCallback.class)); 145 verifyNoMoreInteractions(mProvisioningManager); 146 } 147 148 @Test testColors()149 public void testColors() { 150 Context context = InstrumentationRegistry.getTargetContext(); 151 152 // default color Managed Profile (MP) 153 assertColorsCorrect(PROFILE_OWNER_INTENT, context.getColor(DEFAULT_COLOR_ID_MP)); 154 155 // default color Device Owner (DO) 156 assertColorsCorrect(DEVICE_OWNER_INTENT, context.getColor(DEFAULT_COLOR_ID_DO)); 157 158 // custom color for both cases (MP, DO) 159 int targetColor = Color.parseColor("#d40000"); // any color (except default) would do 160 for (String action : asList(ACTION_PROVISION_MANAGED_PROFILE, 161 ACTION_PROVISION_MANAGED_DEVICE)) { 162 ProvisioningParams provisioningParams = new ProvisioningParams.Builder() 163 .setProvisioningAction(action) 164 .setDeviceAdminComponentName(ADMIN) 165 .setMainColor(targetColor) 166 .build(); 167 assertColorsCorrect(new Intent().putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, 168 provisioningParams), targetColor); 169 } 170 } 171 assertColorsCorrect(Intent intent, int color)172 private void assertColorsCorrect(Intent intent, int color) { 173 launchActivityAndWait(intent); 174 Activity activity = mActivityRule.getActivity(); 175 176 CustomizationVerifier customizationVerifier = new CustomizationVerifier(activity); 177 customizationVerifier.assertStatusBarColorCorrect(color); 178 customizationVerifier.assertDefaultLogoCorrect(color); 179 customizationVerifier.assertProgressBarColorCorrect(color); 180 181 activity.finish(); 182 } 183 184 @Test testCustomLogo()185 public void testCustomLogo() throws IOException { 186 assertCustomLogoCorrect(PROFILE_OWNER_INTENT); 187 assertCustomLogoCorrect(DEVICE_OWNER_INTENT); 188 } 189 assertCustomLogoCorrect(Intent intent)190 private void assertCustomLogoCorrect(Intent intent) throws IOException { 191 UriBitmap targetLogo = UriBitmap.createSimpleInstance(); 192 saveOrganisationLogo(InstrumentationRegistry.getTargetContext(), targetLogo.getUri()); 193 launchActivityAndWait(intent); 194 ProvisioningActivity activity = mActivityRule.getActivity(); 195 new CustomizationVerifier(activity).assertCustomLogoCorrect(targetLogo.getBitmap()); 196 activity.finish(); 197 } 198 199 @Test testSavedInstanceState()200 public void testSavedInstanceState() throws Throwable { 201 // GIVEN the activity was launched with a profile owner intent 202 launchActivityAndWait(PROFILE_OWNER_INTENT); 203 204 // THEN the provisioning process should be initiated 205 verify(mProvisioningManager).maybeStartProvisioning(PROFILE_OWNER_PARAMS); 206 207 // WHEN the activity is recreated with a saved instance state 208 mActivityRule.runOnUiThread(() -> { 209 Bundle bundle = new Bundle(); 210 InstrumentationRegistry.getInstrumentation() 211 .callActivityOnSaveInstanceState(mActivityRule.getActivity(), bundle); 212 InstrumentationRegistry.getInstrumentation() 213 .callActivityOnCreate(mActivityRule.getActivity(), bundle); 214 }); 215 216 // THEN provisioning should not be initiated again 217 verify(mProvisioningManager).maybeStartProvisioning(PROFILE_OWNER_PARAMS); 218 } 219 220 @Test testPause()221 public void testPause() throws Throwable { 222 // GIVEN the activity was launched with a profile owner intent 223 launchActivityAndWait(PROFILE_OWNER_INTENT); 224 225 // WHEN the activity is paused 226 mActivityRule.runOnUiThread(() -> { 227 InstrumentationRegistry.getInstrumentation() 228 .callActivityOnPause(mActivityRule.getActivity()); 229 }); 230 231 // THEN the listener is unregistered 232 verify(mProvisioningManager).unregisterListener(any(ProvisioningManagerCallback.class)); 233 } 234 235 @Test testErrorNoFactoryReset()236 public void testErrorNoFactoryReset() throws Throwable { 237 // GIVEN the activity was launched with a profile owner intent 238 launchActivityAndWait(PROFILE_OWNER_INTENT); 239 240 // WHEN an error occurred that does not require factory reset 241 final int errorMsgId = R.string.managed_provisioning_error_text; 242 mActivityRule.runOnUiThread(() -> mActivityRule.getActivity().error(R.string.cant_set_up_device, errorMsgId, false)); 243 244 // THEN the UI should show an error dialog 245 onView(withText(errorMsgId)).check(matches(isDisplayed())); 246 247 // WHEN clicking ok 248 onView(withId(android.R.id.button1)) 249 .check(matches(withText(R.string.device_owner_error_ok))) 250 .perform(click()); 251 252 // THEN the activity should be finishing 253 assertTrue(mActivityRule.getActivity().isFinishing()); 254 } 255 256 @Test testErrorFactoryReset()257 public void testErrorFactoryReset() throws Throwable { 258 // GIVEN the activity was launched with a device owner intent 259 launchActivityAndWait(DEVICE_OWNER_INTENT); 260 261 // WHEN an error occurred that does not require factory reset 262 final int errorMsgId = R.string.managed_provisioning_error_text; 263 mActivityRule.runOnUiThread(() -> mActivityRule.getActivity().error(R.string.cant_set_up_device, errorMsgId, true)); 264 265 // THEN the UI should show an error dialog 266 onView(withText(errorMsgId)).check(matches(isDisplayed())); 267 268 // WHEN clicking the ok button that says that factory reset is required 269 onView(withId(android.R.id.button1)) 270 .check(matches(withText(R.string.reset))) 271 .perform(click()); 272 273 // THEN factory reset should be invoked 274 verify(mUtils).sendFactoryResetBroadcast(any(Context.class), anyString()); 275 } 276 277 @Test testCancelProfileOwner()278 public void testCancelProfileOwner() throws Throwable { 279 // GIVEN the activity was launched with a profile owner intent 280 launchActivityAndWait(PROFILE_OWNER_INTENT); 281 282 // WHEN the user tries to cancel 283 pressBack(); 284 285 // THEN the cancel dialog should be shown 286 onView(withText(R.string.profile_owner_cancel_message)).check(matches(isDisplayed())); 287 288 // WHEN deciding not to cancel 289 onView(withId(android.R.id.button2)) 290 .check(matches(withText(R.string.profile_owner_cancel_cancel))) 291 .perform(click()); 292 293 // THEN the activity should not be finished 294 assertFalse(mActivityRule.getActivity().isFinishing()); 295 296 // WHEN the user tries to cancel 297 pressBack(); 298 299 // THEN the cancel dialog should be shown 300 onView(withText(R.string.profile_owner_cancel_message)).check(matches(isDisplayed())); 301 302 // WHEN deciding to cancel 303 onView(withId(android.R.id.button1)) 304 .check(matches(withText(R.string.profile_owner_cancel_ok))) 305 .perform(click()); 306 307 // THEN the manager should be informed 308 verify(mProvisioningManager).cancelProvisioning(); 309 310 // THEN the activity should be finished 311 assertTrue(mActivityRule.getActivity().isFinishing()); 312 } 313 314 @Test testCancelProfileOwner_CompProvisioningWithSkipConsent()315 public void testCancelProfileOwner_CompProvisioningWithSkipConsent() throws Throwable { 316 // GIVEN launching profile intent with skipping user consent 317 ProvisioningParams params = new ProvisioningParams.Builder() 318 .setProvisioningAction(ACTION_PROVISION_MANAGED_PROFILE) 319 .setDeviceAdminComponentName(ADMIN) 320 .setSkipUserConsent(true) 321 .build(); 322 Intent intent = new Intent() 323 .putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, params); 324 launchActivityAndWait(new Intent(intent)); 325 326 // WHEN the user tries to cancel 327 mActivityRule.runOnUiThread(() -> mActivityRule.getActivity().onBackPressed()); 328 329 // THEN never unregistering ProvisioningManager 330 verify(mProvisioningManager, never()).unregisterListener( 331 any(ProvisioningManagerCallback.class)); 332 } 333 334 @Test testCancelProfileOwner_CompProvisioningWithoutSkipConsent()335 public void testCancelProfileOwner_CompProvisioningWithoutSkipConsent() throws Throwable { 336 // GIVEN launching profile intent without skipping user consent 337 ProvisioningParams params = new ProvisioningParams.Builder() 338 .setProvisioningAction(ACTION_PROVISION_MANAGED_PROFILE) 339 .setDeviceAdminComponentName(ADMIN) 340 .setSkipUserConsent(false) 341 .build(); 342 Intent intent = new Intent() 343 .putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, params); 344 launchActivityAndWait(new Intent(intent)); 345 346 // WHEN the user tries to cancel 347 mActivityRule.runOnUiThread(() -> mActivityRule.getActivity().onBackPressed()); 348 349 // THEN unregistering ProvisioningManager 350 verify(mProvisioningManager).unregisterListener(any(ProvisioningManagerCallback.class)); 351 352 // THEN the cancel dialog should be shown 353 onView(withText(R.string.profile_owner_cancel_message)).check(matches(isDisplayed())); 354 } 355 356 @Test testCancelDeviceOwner()357 public void testCancelDeviceOwner() throws Throwable { 358 // GIVEN the activity was launched with a device owner intent 359 launchActivityAndWait(DEVICE_OWNER_INTENT); 360 361 // WHEN the user tries to cancel 362 pressBack(); 363 364 // THEN the cancel dialog should be shown 365 onView(withText(R.string.stop_setup_reset_device_question)).check(matches(isDisplayed())); 366 onView(withText(R.string.this_will_reset_take_back_first_screen)) 367 .check(matches(isDisplayed())); 368 369 // WHEN deciding not to cancel 370 onView(withId(android.R.id.button2)) 371 .check(matches(withText(R.string.device_owner_cancel_cancel))) 372 .perform(click()); 373 374 // THEN the activity should not be finished 375 assertFalse(mActivityRule.getActivity().isFinishing()); 376 377 // WHEN the user tries to cancel 378 pressBack(); 379 380 // THEN the cancel dialog should be shown 381 onView(withText(R.string.stop_setup_reset_device_question)).check(matches(isDisplayed())); 382 383 // WHEN deciding to cancel 384 onView(withId(android.R.id.button1)) 385 .check(matches(withText(R.string.reset))) 386 .perform(click()); 387 388 // THEN factory reset should be invoked 389 verify(mUtils).sendFactoryResetBroadcast(any(Context.class), anyString()); 390 } 391 392 @Test testSuccess()393 public void testSuccess() throws Throwable { 394 // GIVEN the activity was launched with a profile owner intent 395 launchActivityAndWait(PROFILE_OWNER_INTENT); 396 397 // WHEN preFinalization is completed 398 mActivityRule.runOnUiThread(() -> mActivityRule.getActivity().preFinalizationCompleted()); 399 400 // THEN the activity should finish 401 assertTrue(mActivityRule.getActivity().isFinishing()); 402 } 403 launchActivityAndWait(Intent intent)404 private void launchActivityAndWait(Intent intent) { 405 mActivityRule.launchActivity(intent); 406 onView(withId(R.id.setup_wizard_layout)); 407 } 408 } 409