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