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 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 22 import static com.android.managedprovisioning.provisioning.AbstractProvisioningActivity.CANCEL_PROVISIONING_DIALOG_OK; 23 import static com.android.managedprovisioning.provisioning.AbstractProvisioningActivity.ERROR_DIALOG_OK; 24 import static com.android.managedprovisioning.provisioning.AbstractProvisioningActivity.ERROR_DIALOG_RESET; 25 import static com.android.managedprovisioning.provisioning.ProvisioningActivity.RESULT_CODE_DEVICE_OWNER_SET; 26 import static com.android.managedprovisioning.provisioning.ProvisioningActivity.RESULT_CODE_WORK_PROFILE_CREATED; 27 28 import static com.google.common.truth.Truth.assertThat; 29 30 import static org.mockito.Mockito.never; 31 import static org.mockito.Mockito.verify; 32 import static org.robolectric.Shadows.shadowOf; 33 import static org.robolectric.shadows.ShadowUserManager.FLAG_MANAGED_PROFILE; 34 35 import android.app.Activity; 36 import android.app.Application; 37 import android.app.DialogFragment; 38 import android.app.Fragment; 39 import android.app.admin.DevicePolicyManager; 40 import android.content.ComponentName; 41 import android.content.Context; 42 import android.content.Intent; 43 import android.graphics.Bitmap; 44 import android.graphics.Color; 45 import android.graphics.PorterDuffColorFilter; 46 import android.graphics.drawable.BitmapDrawable; 47 import android.graphics.drawable.Drawable; 48 import android.graphics.drawable.VectorDrawable; 49 import android.net.Uri; 50 import android.os.UserManager; 51 import android.widget.ImageView; 52 53 import androidx.test.core.content.pm.ApplicationInfoBuilder; 54 import androidx.test.core.content.pm.PackageInfoBuilder; 55 56 import com.android.managedprovisioning.R; 57 import com.android.managedprovisioning.model.ProvisioningParams; 58 59 import org.junit.Ignore; 60 import org.junit.Test; 61 import org.junit.runner.RunWith; 62 import org.mockito.Mockito; 63 import org.robolectric.Robolectric; 64 import org.robolectric.RobolectricTestRunner; 65 import org.robolectric.RuntimeEnvironment; 66 67 import java.io.ByteArrayInputStream; 68 import java.io.ByteArrayOutputStream; 69 import java.io.InputStream; 70 import java.util.List; 71 72 /** 73 * Robolectric tests for {@link ProvisioningActivity}. 74 */ 75 @RunWith(RobolectricTestRunner.class) 76 public class ProvisioningActivityRoboTest { 77 78 private static final String ADMIN_PACKAGE = "com.test.admin"; 79 private static final ComponentName ADMIN = new ComponentName(ADMIN_PACKAGE, ".Receiver"); 80 private static final ProvisioningParams DEVICE_OWNER_PARAMS = new ProvisioningParams.Builder() 81 .setProvisioningAction(ACTION_PROVISION_MANAGED_DEVICE) 82 .setDeviceAdminComponentName(ADMIN) 83 .build(); 84 private static final ProvisioningParams PROFILE_OWNER_PARAMS = new ProvisioningParams.Builder() 85 .setProvisioningAction(ACTION_PROVISION_MANAGED_PROFILE) 86 .setDeviceAdminComponentName(ADMIN) 87 .build(); 88 private static final ProvisioningParams QR_PROVISIONING_PARAMS_DO = new ProvisioningParams.Builder() 89 .setProvisioningAction(ACTION_PROVISION_MANAGED_DEVICE) 90 .setDeviceAdminComponentName(ADMIN) 91 .setIsOrganizationOwnedProvisioning(true) 92 .setFlowType(ProvisioningParams.FLOW_TYPE_ADMIN_INTEGRATED) 93 .build(); 94 private static final ProvisioningParams QR_PROVISIONING_PARAMS_PO = new ProvisioningParams.Builder() 95 .setProvisioningAction(ACTION_PROVISION_MANAGED_PROFILE) 96 .setDeviceAdminComponentName(ADMIN) 97 .setIsOrganizationOwnedProvisioning(true) 98 .setFlowType(ProvisioningParams.FLOW_TYPE_ADMIN_INTEGRATED) 99 .build(); 100 private static final Intent PROFILE_OWNER_INTENT = new Intent() 101 .putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, PROFILE_OWNER_PARAMS); 102 private static final Intent DEVICE_OWNER_INTENT = new Intent() 103 .putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, DEVICE_OWNER_PARAMS); 104 private static final Intent ADMIN_INTEGRATED_FLOW_INTENT_PO = new Intent() 105 .putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, QR_PROVISIONING_PARAMS_PO); 106 private static final Intent ADMIN_INTEGRATED_FLOW_INTENT_DO = new Intent() 107 .putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, QR_PROVISIONING_PARAMS_DO); 108 private static final int ERROR_MESSAGE_ID = R.string.managed_provisioning_error_text; 109 private static final int DEFAULT_LOGO_COLOR = -15043608; 110 private static final int CUSTOM_COLOR = Color.parseColor("#d40000"); 111 private static final Uri LOGO_URI = Uri.parse("http://logo"); 112 113 private ProvisioningManager mMockProvisioningManager = Mockito.mock(ProvisioningManager.class); 114 private Application mContext = RuntimeEnvironment.application; 115 116 @Test error_noFactoryReset_showsDialogue()117 public void error_noFactoryReset_showsDialogue() { 118 final ProvisioningActivity activity = 119 Robolectric.buildActivity(ProvisioningActivity.class, PROFILE_OWNER_INTENT).get(); 120 121 activity.error(R.string.cant_set_up_device, ERROR_MESSAGE_ID, /* resetRequired= */ false); 122 123 final Fragment dialog = activity.getFragmentManager().findFragmentByTag(ERROR_DIALOG_OK); 124 assertThat(dialog).isNotNull(); 125 } 126 127 @Test error_noFactoryReset_doesNotReset()128 public void error_noFactoryReset_doesNotReset() throws Exception { 129 final ProvisioningActivity activity = 130 Robolectric.buildActivity(ProvisioningActivity.class, PROFILE_OWNER_INTENT).get(); 131 activity.error(R.string.cant_set_up_device, ERROR_MESSAGE_ID, /* resetRequired= */ false); 132 133 final Fragment dialog = activity.getFragmentManager().findFragmentByTag(ERROR_DIALOG_OK); 134 clickOnPositiveButton(activity, (DialogFragment) dialog); 135 136 final List<Intent> intents = shadowOf(mContext).getBroadcastIntents(); 137 assertThat(intentsContainsAction(intents, Intent.ACTION_FACTORY_RESET)).isFalse(); 138 } 139 140 @Test error_factoryReset_showsDialogue()141 public void error_factoryReset_showsDialogue() { 142 final ProvisioningActivity activity = 143 Robolectric.buildActivity(ProvisioningActivity.class, PROFILE_OWNER_INTENT).get(); 144 145 activity.error(R.string.cant_set_up_device, ERROR_MESSAGE_ID, /* resetRequired= */ true); 146 147 final Fragment dialog = activity.getFragmentManager().findFragmentByTag(ERROR_DIALOG_RESET); 148 assertThat(dialog).isNotNull(); 149 } 150 151 @Test error_factoryReset_resets()152 public void error_factoryReset_resets() throws Exception { 153 DevicePolicyManager devicePolicyManager = 154 mContext.getSystemService(DevicePolicyManager.class); 155 final ProvisioningActivity activity = 156 Robolectric.buildActivity(ProvisioningActivity.class, PROFILE_OWNER_INTENT).get(); 157 activity.error(R.string.cant_set_up_device, ERROR_MESSAGE_ID, /* resetRequired= */ true); 158 159 final Fragment dialog = activity.getFragmentManager().findFragmentByTag(ERROR_DIALOG_RESET); 160 clickOnPositiveButton(activity, (DialogFragment) dialog); 161 162 assertThat(shadowOf(devicePolicyManager).getWipeCalledTimes()).isEqualTo(1); 163 } 164 165 @Ignore("b/181326453") 166 @Test profileOwnerIntent_usesDefaultLogo()167 public void profileOwnerIntent_usesDefaultLogo() throws Throwable { 168 final ProvisioningActivity activity = 169 Robolectric.buildActivity(ProvisioningActivity.class, PROFILE_OWNER_INTENT) 170 .setup().get(); 171 172 assertUsesDefaultLogo(activity); 173 } 174 175 @Ignore("b/181326453") 176 @Test profileOwnerIntent_setCustomLogo_usesCustomLogo()177 public void profileOwnerIntent_setCustomLogo_usesCustomLogo() throws Throwable { 178 setupCustomLogo(mContext, LOGO_URI); 179 180 final ProvisioningActivity activity = 181 Robolectric.buildActivity(ProvisioningActivity.class, PROFILE_OWNER_INTENT) 182 .setup().get(); 183 184 assertUsesCustomLogo(activity); 185 } 186 187 @Ignore("b/181326453") 188 @Test deviceOwnerIntent_usesDefaultLogo()189 public void deviceOwnerIntent_usesDefaultLogo() throws Throwable { 190 final ProvisioningActivity activity = 191 Robolectric.buildActivity(ProvisioningActivity.class, DEVICE_OWNER_INTENT) 192 .setup().get(); 193 194 assertUsesDefaultLogo(activity); 195 } 196 197 @Ignore("b/181326453") 198 @Test deviceOwnerIntent_setCustomLogo_usesCustomLogo()199 public void deviceOwnerIntent_setCustomLogo_usesCustomLogo() throws Throwable { 200 setupCustomLogo(mContext, LOGO_URI); 201 202 final ProvisioningActivity activity = 203 Robolectric.buildActivity(ProvisioningActivity.class, DEVICE_OWNER_INTENT) 204 .setup().get(); 205 206 assertUsesCustomLogo(activity); 207 } 208 209 @Ignore("b/181326453") 210 @Test managedProfileIntent_defaultColor_colorCorrect()211 public void managedProfileIntent_defaultColor_colorCorrect() { 212 assertColorsCorrect( 213 PROFILE_OWNER_INTENT, 214 DEFAULT_LOGO_COLOR); 215 } 216 217 @Ignore("b/181326453") 218 @Test deviceOwnerIntent_defaultColor_colorCorrect()219 public void deviceOwnerIntent_defaultColor_colorCorrect() { 220 assertColorsCorrect( 221 DEVICE_OWNER_INTENT, 222 DEFAULT_LOGO_COLOR); 223 } 224 225 @Ignore("b/181326453") 226 @Test activity_profileOwner_backPressed_showsCancelDialog()227 public void activity_profileOwner_backPressed_showsCancelDialog() throws Throwable { 228 final ProvisioningActivity activity = 229 Robolectric.buildActivity(ProvisioningActivity.class, PROFILE_OWNER_INTENT) 230 .setup().get(); 231 232 activity.onBackPressed(); 233 234 final Fragment dialog = 235 activity.getFragmentManager().findFragmentByTag(CANCEL_PROVISIONING_DIALOG_OK); 236 assertThat(dialog).isNotNull(); 237 } 238 239 @Ignore("b/181326453") 240 @Test activity_profileOwner_backPressed_doNotCancel_doesNotFinishActivity()241 public void activity_profileOwner_backPressed_doNotCancel_doesNotFinishActivity() { 242 final ProvisioningActivity activity = 243 Robolectric.buildActivity(ProvisioningActivity.class, PROFILE_OWNER_INTENT) 244 .setup().get(); 245 246 activity.onBackPressed(); 247 final Fragment dialog = 248 activity.getFragmentManager().findFragmentByTag(CANCEL_PROVISIONING_DIALOG_OK); 249 clickOnNegativeButton(activity, (DialogFragment) dialog); 250 251 assertThat(activity.isFinishing()).isFalse(); 252 } 253 254 @Ignore("b/181326453") 255 @Test activity_profileOwner_backPressed_doNotCancel_doesNotCancelProvisioning()256 public void activity_profileOwner_backPressed_doNotCancel_doesNotCancelProvisioning() { 257 final ProvisioningActivity activity = 258 Robolectric.buildActivity(ProvisioningActivity.class, PROFILE_OWNER_INTENT) 259 .setup().get(); 260 activity.setProvisioningManager(mMockProvisioningManager); 261 262 activity.onBackPressed(); 263 final Fragment dialog = 264 activity.getFragmentManager().findFragmentByTag(CANCEL_PROVISIONING_DIALOG_OK); 265 clickOnNegativeButton(activity, (DialogFragment) dialog); 266 267 verify(mMockProvisioningManager, never()).cancelProvisioning(); 268 } 269 270 @Ignore("b/181326453") 271 @Test activity_profileOwner_backPressed_cancel_doesFinishActivity()272 public void activity_profileOwner_backPressed_cancel_doesFinishActivity() { 273 final ProvisioningActivity activity = 274 Robolectric.buildActivity(ProvisioningActivity.class, PROFILE_OWNER_INTENT) 275 .setup().get(); 276 277 activity.onBackPressed(); 278 final Fragment dialog = 279 activity.getFragmentManager().findFragmentByTag(CANCEL_PROVISIONING_DIALOG_OK); 280 clickOnPositiveButton(activity, (DialogFragment) dialog); 281 282 assertThat(activity.isFinishing()).isTrue(); 283 } 284 285 @Ignore("b/181326453") 286 @Test activity_profileOwner_backPressed_cancel_doesCancelProvisioning()287 public void activity_profileOwner_backPressed_cancel_doesCancelProvisioning() { 288 final ProvisioningActivity activity = 289 Robolectric.buildActivity(ProvisioningActivity.class, PROFILE_OWNER_INTENT) 290 .setup().get(); 291 activity.setProvisioningManager(mMockProvisioningManager); 292 293 activity.onBackPressed(); 294 final Fragment dialog = 295 activity.getFragmentManager().findFragmentByTag(CANCEL_PROVISIONING_DIALOG_OK); 296 clickOnPositiveButton(activity, (DialogFragment) dialog); 297 298 verify(mMockProvisioningManager).cancelProvisioning(); 299 } 300 301 @Ignore("b/181326453") 302 @Test activity_profileOwner_adminIntegrated_returnsIntermediateResult()303 public void activity_profileOwner_adminIntegrated_returnsIntermediateResult() { 304 final ProvisioningActivity activity = 305 Robolectric.buildActivity(ProvisioningActivity.class, 306 ADMIN_INTEGRATED_FLOW_INTENT_PO) 307 .setup().get(); 308 shadowOf(activity.getPackageManager()) 309 .installPackage( 310 PackageInfoBuilder.newBuilder() 311 .setPackageName(ADMIN_PACKAGE) 312 .setApplicationInfo(ApplicationInfoBuilder.newBuilder() 313 .setPackageName(ADMIN_PACKAGE) 314 .build()) 315 .build()); 316 317 318 activity.preFinalizationCompleted(); 319 activity.onAllTransitionsShown(); 320 321 UserManager userManager = activity.getSystemService(UserManager.class); 322 shadowOf(userManager).addProfile(0, 10, "profile name", FLAG_MANAGED_PROFILE); 323 324 activity.onNextButtonClicked(); 325 326 assertThat(activity.isFinishing()).isTrue(); 327 assertThat(shadowOf(activity).getResultCode()).isEqualTo(RESULT_CODE_WORK_PROFILE_CREATED); 328 } 329 330 @Ignore("b/181326453") 331 @Test activity_deviceOwner_adminIntegrated_returnsIntermediateResult()332 public void activity_deviceOwner_adminIntegrated_returnsIntermediateResult() { 333 final ProvisioningActivity activity = 334 Robolectric.buildActivity(ProvisioningActivity.class, 335 ADMIN_INTEGRATED_FLOW_INTENT_DO) 336 .setup().get(); 337 shadowOf(activity.getPackageManager()) 338 .installPackage( 339 PackageInfoBuilder.newBuilder() 340 .setPackageName(ADMIN_PACKAGE) 341 .setApplicationInfo(ApplicationInfoBuilder.newBuilder() 342 .setPackageName(ADMIN_PACKAGE) 343 .build()) 344 .build()); 345 346 347 activity.preFinalizationCompleted(); 348 activity.onAllTransitionsShown(); 349 350 activity.onNextButtonClicked(); 351 352 assertThat(activity.isFinishing()).isTrue(); 353 assertThat(shadowOf(activity).getResultCode()).isEqualTo(RESULT_CODE_DEVICE_OWNER_SET); 354 } 355 356 @Ignore("b/181326453") 357 @Test activity_deviceOwner_notAdminIntegrated_returnsOk()358 public void activity_deviceOwner_notAdminIntegrated_returnsOk() { 359 final ProvisioningActivity activity = 360 Robolectric.buildActivity(ProvisioningActivity.class, 361 DEVICE_OWNER_INTENT) 362 .setup().get(); 363 shadowOf(activity.getPackageManager()) 364 .installPackage( 365 PackageInfoBuilder.newBuilder() 366 .setPackageName(ADMIN_PACKAGE) 367 .setApplicationInfo(ApplicationInfoBuilder.newBuilder() 368 .setPackageName(ADMIN_PACKAGE) 369 .build()) 370 .build()); 371 372 373 activity.preFinalizationCompleted(); 374 activity.onAllTransitionsShown(); 375 376 activity.onNextButtonClicked(); 377 378 assertThat(activity.isFinishing()).isTrue(); 379 assertThat(shadowOf(activity).getResultCode()).isEqualTo(Activity.RESULT_OK); 380 } 381 createProvisioningIntent(String action)382 private Intent createProvisioningIntent(String action) { 383 final ProvisioningParams provisioningParams = new ProvisioningParams.Builder() 384 .setProvisioningAction(action) 385 .setDeviceAdminComponentName(ADMIN) 386 .build(); 387 388 final Intent intent = new Intent(); 389 intent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, provisioningParams); 390 return intent; 391 } 392 assertColorsCorrect(Intent intent, int logoColor)393 private void assertColorsCorrect(Intent intent, int logoColor) { 394 final ProvisioningActivity activity = 395 Robolectric.buildActivity(ProvisioningActivity.class, intent) 396 .setup().get(); 397 398 assertDefaultLogoColorCorrect(activity, logoColor); 399 } 400 assertDefaultLogoColorCorrect(Activity activity, int targetColor)401 private void assertDefaultLogoColorCorrect(Activity activity, int targetColor) { 402 Drawable actualLogo = 403 ((ImageView) activity.findViewById(R.id.sud_layout_icon)).getDrawable(); 404 PorterDuffColorFilter colorFilter = (PorterDuffColorFilter) actualLogo.getColorFilter(); 405 406 assertThat(colorFilter.getColor()).isEqualTo(targetColor); 407 } 408 intentsContainsAction(List<Intent> intents, String action)409 private static boolean intentsContainsAction(List<Intent> intents, String action) { 410 return intents.stream().anyMatch(intent -> intent.getAction().equals(action)); 411 } 412 clickOnOkButton(ProvisioningActivity activity, DialogFragment dialog)413 private void clickOnOkButton(ProvisioningActivity activity, DialogFragment dialog) { 414 // TODO(135181317): This should be replaced by 415 // activity.findViewById(android.R.id.button1).performClick(); 416 activity.onPositiveButtonClick(dialog); 417 } 418 setupCustomLogo(Context context, Uri logoUri)419 private static void setupCustomLogo(Context context, Uri logoUri) { 420 Bitmap bitmap = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888); 421 InputStream inputStream = bitmapToInputStream(bitmap); 422 shadowOf(context.getContentResolver()).registerInputStream(logoUri, inputStream); 423 } 424 bitmapToInputStream(Bitmap bitmap)425 private static InputStream bitmapToInputStream(Bitmap bitmap) { 426 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 427 bitmap.compress(Bitmap.CompressFormat.PNG, 0 /* ignored for PNG */, bos); 428 byte[] bitmapdata = bos.toByteArray(); 429 return new ByteArrayInputStream(bitmapdata); 430 } 431 assertUsesDefaultLogo(Activity activity)432 private static void assertUsesDefaultLogo(Activity activity) { 433 final ImageView imageView = activity.findViewById(R.id.sud_layout_icon); 434 // We default to a vector logo 435 assertThat(imageView.getDrawable()).isInstanceOf(VectorDrawable.class); 436 } 437 assertUsesCustomLogo(Activity activity)438 private static void assertUsesCustomLogo(Activity activity) { 439 final ImageView imageView = activity.findViewById(R.id.sud_layout_icon); 440 // The custom logo we have set is a bitmap 441 assertThat(imageView.getDrawable()).isInstanceOf(BitmapDrawable.class); 442 } 443 clickOnPositiveButton(ProvisioningActivity activity, DialogFragment dialog)444 private void clickOnPositiveButton(ProvisioningActivity activity, DialogFragment dialog) { 445 // TODO(135181317): This should be replaced by 446 // activity.findViewById(android.R.id.button1).performClick(); 447 448 activity.onPositiveButtonClick(dialog); 449 } 450 clickOnNegativeButton(ProvisioningActivity activity, DialogFragment dialog)451 private void clickOnNegativeButton(ProvisioningActivity activity, DialogFragment dialog) { 452 // TODO(135181317): This should be replaced by 453 // activity.findViewById(android.R.id.button2).performClick(); 454 activity.onNegativeButtonClick(dialog); 455 } 456 } 457