• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_DEVICE_FROM_TRUSTED_SOURCE;
21 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
22 import static android.app.admin.DevicePolicyManager.ACTION_STATE_USER_SETUP_COMPLETE;
23 import static android.support.test.espresso.Espresso.onView;
24 import static android.support.test.espresso.Espresso.pressBack;
25 import static android.support.test.espresso.action.ViewActions.click;
26 import static android.support.test.espresso.assertion.ViewAssertions.matches;
27 import static android.support.test.espresso.intent.Intents.intended;
28 import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction;
29 import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
30 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
31 import static android.support.test.espresso.matcher.ViewMatchers.withId;
32 import static android.support.test.espresso.matcher.ViewMatchers.withText;
33 import static com.android.managedprovisioning.common.LogoUtils.saveOrganisationLogo;
34 import static com.android.managedprovisioning.model.CustomizationParams.DEFAULT_COLOR_ID_DO;
35 import static com.android.managedprovisioning.model.CustomizationParams.DEFAULT_COLOR_ID_MP;
36 import static java.util.Arrays.asList;
37 import static org.hamcrest.core.AllOf.allOf;
38 import static org.junit.Assert.assertFalse;
39 import static org.junit.Assert.assertTrue;
40 import static org.mockito.Matchers.any;
41 import static org.mockito.Matchers.anyString;
42 import static org.mockito.Matchers.eq;
43 import static org.mockito.Mockito.never;
44 import static org.mockito.Mockito.verify;
45 import static org.mockito.Mockito.verifyNoMoreInteractions;
46 import static org.mockito.Mockito.when;
47 
48 import android.Manifest.permission;
49 import android.app.Activity;
50 import android.content.ComponentName;
51 import android.content.Context;
52 import android.content.Intent;
53 import android.content.pm.ActivityInfo;
54 import android.content.pm.PackageManager;
55 import android.content.pm.ResolveInfo;
56 import android.graphics.Color;
57 import android.os.Bundle;
58 import android.provider.Settings;
59 import android.support.test.InstrumentationRegistry;
60 import android.support.test.espresso.intent.Intents;
61 import android.support.test.espresso.intent.rule.IntentsTestRule;
62 import android.support.test.filters.SmallTest;
63 import android.support.test.runner.lifecycle.Stage;
64 import com.android.managedprovisioning.R;
65 import com.android.managedprovisioning.TestInstrumentationRunner;
66 import com.android.managedprovisioning.common.CustomizationVerifier;
67 import com.android.managedprovisioning.common.LogoUtils;
68 import com.android.managedprovisioning.common.UriBitmap;
69 import com.android.managedprovisioning.common.Utils;
70 import com.android.managedprovisioning.model.ProvisioningParams;
71 import com.android.managedprovisioning.testcommon.ActivityLifecycleWaiter;
72 import java.util.ArrayList;
73 import java.util.List;
74 import org.junit.After;
75 import org.junit.AfterClass;
76 import org.junit.Before;
77 import org.junit.BeforeClass;
78 import org.junit.Rule;
79 import org.junit.Test;
80 import org.mockito.Mock;
81 import org.mockito.MockitoAnnotations;
82 import org.mockito.hamcrest.MockitoHamcrest;
83 
84 /**
85  * Unit tests for {@link ProvisioningActivity}.
86  */
87 @SmallTest
88 public class ProvisioningActivityTest {
89     private static final String ADMIN_PACKAGE = "com.test.admin";
90     private static final String TEST_PACKAGE = "com.android.managedprovisioning.tests";
91     private static final ComponentName ADMIN = new ComponentName(ADMIN_PACKAGE, ".Receiver");
92     private static final ComponentName TEST_ACTIVITY = new ComponentName(TEST_PACKAGE,
93             EmptyActivity.class.getCanonicalName());
94     public static final ProvisioningParams PROFILE_OWNER_PARAMS = new ProvisioningParams.Builder()
95             .setProvisioningAction(ACTION_PROVISION_MANAGED_PROFILE)
96             .setDeviceAdminComponentName(ADMIN)
97             .build();
98     public static final ProvisioningParams DEVICE_OWNER_PARAMS = new ProvisioningParams.Builder()
99             .setProvisioningAction(ACTION_PROVISION_MANAGED_DEVICE)
100             .setDeviceAdminComponentName(ADMIN)
101             .build();
102     private static final ProvisioningParams NFC_PARAMS = new ProvisioningParams.Builder()
103             .setProvisioningAction(ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE)
104             .setDeviceAdminComponentName(ADMIN)
105             .setStartedByTrustedSource(true)
106             .setIsNfc(true)
107             .build();
108     private static final Intent PROFILE_OWNER_INTENT = new Intent()
109             .putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, PROFILE_OWNER_PARAMS);
110     private static final Intent DEVICE_OWNER_INTENT = new Intent()
111             .putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, DEVICE_OWNER_PARAMS);
112     private static final Intent NFC_INTENT = new Intent()
113             .putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, NFC_PARAMS);
114 
115     private static class CustomIntentsTestRule extends IntentsTestRule<ProvisioningActivity> {
116         private boolean mIsActivityRunning = false;
CustomIntentsTestRule()117         private CustomIntentsTestRule() {
118             super(ProvisioningActivity.class, true /* Initial touch mode  */,
119                     false /* Lazily launch activity */);
120         }
121 
122         @Override
afterActivityLaunched()123         protected synchronized void afterActivityLaunched() {
124             mIsActivityRunning = true;
125             super.afterActivityLaunched();
126         }
127 
128         @Override
afterActivityFinished()129         public synchronized void afterActivityFinished() {
130             // Temp fix for b/37663530
131             if (mIsActivityRunning) {
132                 super.afterActivityFinished();
133                 mIsActivityRunning = false;
134             }
135         }
136     }
137 
138     @Rule
139     public CustomIntentsTestRule mActivityRule = new CustomIntentsTestRule();
140 
141     @Mock private ProvisioningManager mProvisioningManager;
142     @Mock private PackageManager mPackageManager;
143     @Mock private Utils mUtils;
144     private static int mRotationLocked;
145 
146     @BeforeClass
setUpClass()147     public static void setUpClass() {
148         // Stop the activity from rotating in order to keep hold of the context
149         Context context = InstrumentationRegistry.getTargetContext();
150 
151         mRotationLocked = Settings.System.getInt(context.getContentResolver(),
152                 Settings.System.ACCELEROMETER_ROTATION, 0);
153         Settings.System.putInt(context.getContentResolver(),
154                 Settings.System.ACCELEROMETER_ROTATION, 0);
155     }
156 
157     @AfterClass
tearDownClass()158     public static void tearDownClass() {
159         // Reset the rotation value back to what it was before the test
160         Context context = InstrumentationRegistry.getTargetContext();
161 
162         Settings.System.putInt(context.getContentResolver(),
163                 Settings.System.ACCELEROMETER_ROTATION, mRotationLocked);
164     }
165 
166     @Before
setUp()167     public void setUp() {
168         MockitoAnnotations.initMocks(this);
169 
170         TestInstrumentationRunner.registerReplacedActivity(ProvisioningActivity.class,
171                 (classLoader, className, intent) ->
172                         new ProvisioningActivity(mProvisioningManager, mUtils) {
173                             @Override
174                             public PackageManager getPackageManager() {
175                                 return mPackageManager;
176                             }
177                         });
178         // LogoUtils cached icon globally. Clean-up the cache
179         LogoUtils.cleanUp(InstrumentationRegistry.getTargetContext());
180     }
181 
182     @After
tearDown()183     public void tearDown() {
184         TestInstrumentationRunner.unregisterReplacedActivity(ProvisioningActivity.class);
185     }
186 
187     @Test
testLaunch()188     public void testLaunch() {
189         // GIVEN the activity was launched with a profile owner intent
190         launchActivityAndWait(PROFILE_OWNER_INTENT);
191 
192         // THEN the provisioning process should be initiated
193         verify(mProvisioningManager).maybeStartProvisioning(PROFILE_OWNER_PARAMS);
194 
195         // THEN the activity should start listening for provisioning updates
196         verify(mProvisioningManager).registerListener(any(ProvisioningManagerCallback.class));
197         verifyNoMoreInteractions(mProvisioningManager);
198     }
199 
200     @Test
testColors()201     public void testColors() throws Throwable {
202         Context context = InstrumentationRegistry.getTargetContext();
203 
204         // default color Managed Profile (MP)
205         assertColorsCorrect(PROFILE_OWNER_INTENT, context.getColor(DEFAULT_COLOR_ID_MP));
206 
207         // default color Device Owner (DO)
208         assertColorsCorrect(DEVICE_OWNER_INTENT, context.getColor(DEFAULT_COLOR_ID_DO));
209 
210         // custom color for both cases (MP, DO)
211         int targetColor = Color.parseColor("#d40000"); // any color (except default) would do
212         for (String action : asList(ACTION_PROVISION_MANAGED_PROFILE,
213                 ACTION_PROVISION_MANAGED_DEVICE)) {
214             ProvisioningParams provisioningParams = new ProvisioningParams.Builder()
215                     .setProvisioningAction(action)
216                     .setDeviceAdminComponentName(ADMIN)
217                     .setMainColor(targetColor)
218                     .build();
219             assertColorsCorrect(new Intent().putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS,
220                     provisioningParams), targetColor);
221         }
222     }
223 
assertColorsCorrect(Intent intent, int color)224     private void assertColorsCorrect(Intent intent, int color) throws Throwable {
225         launchActivityAndWait(intent);
226         Activity activity = mActivityRule.getActivity();
227 
228         CustomizationVerifier customizationVerifier = new CustomizationVerifier(activity);
229         customizationVerifier.assertStatusBarColorCorrect(color);
230         customizationVerifier.assertDefaultLogoCorrect(color);
231         customizationVerifier.assertProgressBarColorCorrect(color);
232 
233         finishAndWait();
234     }
235 
finishAndWait()236     private void finishAndWait() throws Throwable {
237         Activity activity = mActivityRule.getActivity();
238         ActivityLifecycleWaiter waiter = new ActivityLifecycleWaiter(activity, Stage.DESTROYED);
239         mActivityRule.runOnUiThread(() -> activity.finish());
240         waiter.waitForStage();
241         mActivityRule.afterActivityFinished();
242     }
243 
244     @Test
testCustomLogo_profileOwner()245     public void testCustomLogo_profileOwner() throws Throwable {
246         assertCustomLogoCorrect(PROFILE_OWNER_INTENT);
247     }
248 
249     @Test
testCustomLogo_deviceOwner()250     public void testCustomLogo_deviceOwner() throws Throwable {
251         assertCustomLogoCorrect(PROFILE_OWNER_INTENT);
252     }
253 
assertCustomLogoCorrect(Intent intent)254     private void assertCustomLogoCorrect(Intent intent) throws Throwable {
255         UriBitmap targetLogo = UriBitmap.createSimpleInstance();
256         saveOrganisationLogo(InstrumentationRegistry.getTargetContext(), targetLogo.getUri());
257         launchActivityAndWait(intent);
258         ProvisioningActivity activity = mActivityRule.getActivity();
259         new CustomizationVerifier(activity).assertCustomLogoCorrect(targetLogo.getBitmap());
260         finishAndWait();
261     }
262 
263     @Test
testSavedInstanceState()264     public void testSavedInstanceState() throws Throwable {
265         // GIVEN the activity was launched with a profile owner intent
266         launchActivityAndWait(PROFILE_OWNER_INTENT);
267 
268         // THEN the provisioning process should be initiated
269         verify(mProvisioningManager).maybeStartProvisioning(PROFILE_OWNER_PARAMS);
270 
271         // WHEN the activity is recreated with a saved instance state
272         mActivityRule.runOnUiThread(() -> {
273                     Bundle bundle = new Bundle();
274                     InstrumentationRegistry.getInstrumentation()
275                             .callActivityOnSaveInstanceState(mActivityRule.getActivity(), bundle);
276                     InstrumentationRegistry.getInstrumentation()
277                             .callActivityOnCreate(mActivityRule.getActivity(), bundle);
278                 });
279 
280         // THEN provisioning should not be initiated again
281         verify(mProvisioningManager).maybeStartProvisioning(PROFILE_OWNER_PARAMS);
282     }
283 
284     @Test
testPause()285     public void testPause() throws Throwable {
286         // GIVEN the activity was launched with a profile owner intent
287         launchActivityAndWait(PROFILE_OWNER_INTENT);
288 
289         // WHEN the activity is paused
290         mActivityRule.runOnUiThread(() -> {
291             InstrumentationRegistry.getInstrumentation()
292                     .callActivityOnPause(mActivityRule.getActivity());
293         });
294 
295         // THEN the listener is unregistered
296         verify(mProvisioningManager).unregisterListener(any(ProvisioningManagerCallback.class));
297     }
298 
299     @Test
testErrorNoFactoryReset()300     public void testErrorNoFactoryReset() throws Throwable {
301         // GIVEN the activity was launched with a profile owner intent
302         launchActivityAndWait(PROFILE_OWNER_INTENT);
303 
304         // WHEN an error occurred that does not require factory reset
305         final int errorMsgId = R.string.managed_provisioning_error_text;
306         mActivityRule.runOnUiThread(() -> mActivityRule.getActivity().error(R.string.cant_set_up_device, errorMsgId, false));
307 
308         // THEN the UI should show an error dialog
309         onView(withText(errorMsgId)).check(matches(isDisplayed()));
310 
311         // WHEN clicking ok
312         onView(withId(android.R.id.button1))
313                 .check(matches(withText(R.string.device_owner_error_ok)))
314                 .perform(click());
315 
316         // THEN the activity should be finishing
317         assertTrue(mActivityRule.getActivity().isFinishing());
318     }
319 
320     @Test
testErrorFactoryReset()321     public void testErrorFactoryReset() throws Throwable {
322         // GIVEN the activity was launched with a device owner intent
323         launchActivityAndWait(DEVICE_OWNER_INTENT);
324 
325         // WHEN an error occurred that does not require factory reset
326         final int errorMsgId = R.string.managed_provisioning_error_text;
327         mActivityRule.runOnUiThread(() -> mActivityRule.getActivity().error(R.string.cant_set_up_device, errorMsgId, true));
328 
329         // THEN the UI should show an error dialog
330         onView(withText(errorMsgId)).check(matches(isDisplayed()));
331 
332         // WHEN clicking the ok button that says that factory reset is required
333         onView(withId(android.R.id.button1))
334                 .check(matches(withText(R.string.reset)))
335                 .perform(click());
336 
337         // THEN factory reset should be invoked
338         verify(mUtils).sendFactoryResetBroadcast(any(Context.class), anyString());
339     }
340 
341     @Test
testCancelProfileOwner()342     public void testCancelProfileOwner() throws Throwable {
343         // GIVEN the activity was launched with a profile owner intent
344         launchActivityAndWait(PROFILE_OWNER_INTENT);
345 
346         // WHEN the user tries to cancel
347         pressBack();
348 
349         // THEN the cancel dialog should be shown
350         onView(withText(R.string.profile_owner_cancel_message)).check(matches(isDisplayed()));
351 
352         // WHEN deciding not to cancel
353         onView(withId(android.R.id.button2))
354                 .check(matches(withText(R.string.profile_owner_cancel_cancel)))
355                 .perform(click());
356 
357         // THEN the activity should not be finished
358         assertFalse(mActivityRule.getActivity().isFinishing());
359 
360         // WHEN the user tries to cancel
361         pressBack();
362 
363         // THEN the cancel dialog should be shown
364         onView(withText(R.string.profile_owner_cancel_message)).check(matches(isDisplayed()));
365 
366         // WHEN deciding to cancel
367         onView(withId(android.R.id.button1))
368                 .check(matches(withText(R.string.profile_owner_cancel_ok)))
369                 .perform(click());
370 
371         // THEN the manager should be informed
372         verify(mProvisioningManager).cancelProvisioning();
373 
374         // THEN the activity should be finished
375         assertTrue(mActivityRule.getActivity().isFinishing());
376     }
377 
378     @Test
testCancelProfileOwner_CompProvisioningWithSkipConsent()379     public void testCancelProfileOwner_CompProvisioningWithSkipConsent() throws Throwable {
380         // GIVEN launching profile intent with skipping user consent
381         ProvisioningParams params = new ProvisioningParams.Builder()
382                 .setProvisioningAction(ACTION_PROVISION_MANAGED_PROFILE)
383                 .setDeviceAdminComponentName(ADMIN)
384                 .setSkipUserConsent(true)
385                 .build();
386         Intent intent = new Intent()
387                 .putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, params);
388         launchActivityAndWait(new Intent(intent));
389 
390         // WHEN the user tries to cancel
391         mActivityRule.runOnUiThread(() -> mActivityRule.getActivity().onBackPressed());
392 
393         // THEN never unregistering ProvisioningManager
394         verify(mProvisioningManager, never()).unregisterListener(
395                 any(ProvisioningManagerCallback.class));
396     }
397 
398     @Test
testCancelProfileOwner_CompProvisioningWithoutSkipConsent()399     public void testCancelProfileOwner_CompProvisioningWithoutSkipConsent() throws Throwable {
400         // GIVEN launching profile intent without skipping user consent
401         ProvisioningParams params = new ProvisioningParams.Builder()
402                 .setProvisioningAction(ACTION_PROVISION_MANAGED_PROFILE)
403                 .setDeviceAdminComponentName(ADMIN)
404                 .setSkipUserConsent(false)
405                 .build();
406         Intent intent = new Intent()
407                 .putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, params);
408         launchActivityAndWait(new Intent(intent));
409 
410         // WHEN the user tries to cancel
411         mActivityRule.runOnUiThread(() -> mActivityRule.getActivity().onBackPressed());
412 
413         // THEN unregistering ProvisioningManager
414         verify(mProvisioningManager).unregisterListener(any(ProvisioningManagerCallback.class));
415 
416         // THEN the cancel dialog should be shown
417         onView(withText(R.string.profile_owner_cancel_message)).check(matches(isDisplayed()));
418     }
419 
420     @Test
testCancelDeviceOwner()421     public void testCancelDeviceOwner() throws Throwable {
422         // GIVEN the activity was launched with a device owner intent
423         launchActivityAndWait(DEVICE_OWNER_INTENT);
424 
425         // WHEN the user tries to cancel
426         pressBack();
427 
428         // THEN the cancel dialog should be shown
429         onView(withText(R.string.stop_setup_reset_device_question)).check(matches(isDisplayed()));
430         onView(withText(R.string.this_will_reset_take_back_first_screen))
431                 .check(matches(isDisplayed()));
432 
433         // WHEN deciding not to cancel
434         onView(withId(android.R.id.button2))
435                 .check(matches(withText(R.string.device_owner_cancel_cancel)))
436                 .perform(click());
437 
438         // THEN the activity should not be finished
439         assertFalse(mActivityRule.getActivity().isFinishing());
440 
441         // WHEN the user tries to cancel
442         pressBack();
443 
444         // THEN the cancel dialog should be shown
445         onView(withText(R.string.stop_setup_reset_device_question)).check(matches(isDisplayed()));
446 
447         // WHEN deciding to cancel
448         onView(withId(android.R.id.button1))
449                 .check(matches(withText(R.string.reset)))
450                 .perform(click());
451 
452         // THEN factory reset should be invoked
453         verify(mUtils).sendFactoryResetBroadcast(any(Context.class), anyString());
454     }
455 
456     @Test
testSuccess()457     public void testSuccess() throws Throwable {
458         // GIVEN the activity was launched with a profile owner intent
459         launchActivityAndWait(PROFILE_OWNER_INTENT);
460 
461         // WHEN preFinalization is completed
462         mActivityRule.runOnUiThread(() -> mActivityRule.getActivity().preFinalizationCompleted());
463 
464         // THEN the activity should finish
465         assertTrue(mActivityRule.getActivity().isFinishing());
466     }
467 
468     @Test
testSuccess_Nfc()469     public void testSuccess_Nfc() throws Throwable {
470         // GIVEN queryIntentActivities return test_activity
471         ActivityInfo activityInfo = new ActivityInfo();
472         activityInfo.packageName = TEST_ACTIVITY.getPackageName();
473         activityInfo.name = TEST_ACTIVITY.getClassName();
474         activityInfo.permission = permission.BIND_DEVICE_ADMIN;
475         ResolveInfo resolveInfo = new ResolveInfo();
476         resolveInfo.activityInfo = activityInfo;
477         List<ResolveInfo> resolveInfoList = new ArrayList();
478         resolveInfoList.add(resolveInfo);
479         when(mPackageManager.queryIntentActivities(
480                 MockitoHamcrest.argThat(hasAction(ACTION_STATE_USER_SETUP_COMPLETE)),
481                 eq(0))).thenReturn(resolveInfoList);
482         when(mPackageManager.checkPermission(eq(permission.DISPATCH_PROVISIONING_MESSAGE),
483                 eq(activityInfo.packageName))).thenReturn(PackageManager.PERMISSION_GRANTED);
484 
485         // GIVEN the activity was launched with a nfc intent
486         launchActivityAndWait(NFC_INTENT);
487 
488         // WHEN preFinalization is completed
489         mActivityRule.runOnUiThread(() -> mActivityRule.getActivity().preFinalizationCompleted());
490         // THEN verify starting TEST_ACTIVITY
491         intended(allOf(hasComponent(TEST_ACTIVITY), hasAction(ACTION_STATE_USER_SETUP_COMPLETE)));
492         // THEN the activity should finish
493         assertTrue(mActivityRule.getActivity().isFinishing());
494     }
495 
launchActivityAndWait(Intent intent)496     private void launchActivityAndWait(Intent intent) {
497         mActivityRule.launchActivity(intent);
498         onView(withId(R.id.setup_wizard_layout));
499     }
500 }
501