1 /* 2 * Copyright (C) 2012 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.cts.verifier.managedprovisioning; 18 19 import static android.Manifest.permission.POST_NOTIFICATIONS; 20 import static android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES; 21 import static android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY; 22 23 import android.Manifest; 24 import android.app.Activity; 25 import android.app.KeyguardManager; 26 import android.app.Notification; 27 import android.app.NotificationChannel; 28 import android.app.NotificationManager; 29 import android.app.admin.DevicePolicyManager; 30 import android.content.ComponentName; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.pm.PackageManager; 34 import android.net.Uri; 35 import android.os.Bundle; 36 import android.os.Handler; 37 import android.os.UserManager; 38 import android.provider.MediaStore; 39 import android.util.Log; 40 import android.widget.Toast; 41 42 import androidx.annotation.NonNull; 43 import androidx.core.app.ActivityCompat; 44 import androidx.core.content.ContextCompat; 45 import androidx.core.content.FileProvider; 46 import androidx.core.util.Pair; 47 48 import com.android.cts.verifier.R; 49 import com.android.cts.verifier.managedprovisioning.ByodPresentMediaDialog.DialogCallback; 50 51 import java.io.File; 52 import java.util.ArrayList; 53 54 /** 55 * A helper activity from the managed profile side that responds to requests from CTS verifier in 56 * primary user. Profile owner APIs are accessible inside this activity (given this activity is 57 * started within the work profile). Its current functionalities include making sure the profile 58 * owner is setup correctly, removing the work profile upon request, and verifying the image and 59 * video capture functionality. 60 * 61 * Note: We have to use a test activity because cross-profile intents only work for activities. 62 */ 63 public class ByodHelperActivity extends Activity 64 implements DialogCallback, ActivityCompat.OnRequestPermissionsResultCallback { 65 66 static final String TAG = "ByodHelperActivity"; 67 68 // Primary -> managed intent: query if the profile owner has been set up. 69 public static final String ACTION_QUERY_PROFILE_OWNER = "com.android.cts.verifier.managedprovisioning.BYOD_QUERY"; 70 // Managed -> primary intent: update profile owner test status in primary's CtsVerifer 71 public static final String ACTION_PROFILE_OWNER_STATUS = "com.android.cts.verifier.managedprovisioning.BYOD_STATUS"; 72 // Primary -> managed intent: request to delete the current profile 73 public static final String ACTION_REMOVE_MANAGED_PROFILE = "com.android.cts.verifier.managedprovisioning.BYOD_REMOVE"; 74 // Primary -> managed intent: request to capture and check an image 75 public static final String ACTION_CAPTURE_AND_CHECK_IMAGE = "com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_IMAGE"; 76 // Primary -> managed intent: request to capture and check a video with custom output path 77 public static final String ACTION_CAPTURE_AND_CHECK_VIDEO_WITH_EXTRA_OUTPUT = "com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_VIDEO_WITH_EXTRA_OUTPUT"; 78 // Primary -> managed intent: request to capture and check a video without custom output path 79 public static final String ACTION_CAPTURE_AND_CHECK_VIDEO_WITHOUT_EXTRA_OUTPUT = "com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_VIDEO_WITHOUT_EXTRA_OUTPUT"; 80 // Primary -> managed intent: request to capture and check an audio recording 81 public static final String ACTION_CAPTURE_AND_CHECK_AUDIO = "com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_AUDIO"; 82 public static final String ACTION_KEYGUARD_DISABLED_FEATURES = 83 "com.android.cts.verifier.managedprovisioning.BYOD_KEYGUARD_DISABLED_FEATURES"; 84 public static final String ACTION_LOCKNOW = 85 "com.android.cts.verifier.managedprovisioning.BYOD_LOCKNOW"; 86 public static final String ACTION_TEST_NFC_BEAM = "com.android.cts.verifier.managedprovisioning.TEST_NFC_BEAM"; 87 88 public static final String EXTRA_PROVISIONED = "extra_provisioned"; 89 public static final String EXTRA_PARAMETER_1 = "extra_parameter_1"; 90 91 // Primary -> managed intent: check if the disk of the device is encrypted 92 public static final String ACTION_CHECK_DISK_ENCRYPTION = 93 "com.android.cts.verifier.managedprovisioning.action.BYOD_CHECK_DISK_ENCRYPTION"; 94 // Managed -> primary intent: update disk encryption status in primary's CtsVerifier 95 public static final String ACTION_DISK_ENCRYPTION_STATUS = 96 "com.android.cts.verifier.managedprovisioning.action.BYOD_DISK_ENCRYPTION_STATUS"; 97 // Int extra field indicating the encryption status of the device storage 98 public static final String EXTRA_ENCRYPTION_STATUS = "extra_encryption_status"; 99 100 // Primary -> managed intent: set unknown sources restriction and install package 101 public static final String ACTION_INSTALL_APK = "com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK"; 102 public static final String EXTRA_ALLOW_NON_MARKET_APPS = "allow_non_market_apps"; 103 public static final String ACTION_INSTALL_APK_WORK_PROFILE_GLOBAL_RESTRICTION = "com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK_WORK_PROFILE_GLOBAL_RESTRICTION"; 104 public static final String EXTRA_ALLOW_NON_MARKET_APPS_DEVICE_WIDE = "allow_non_market_apps_device_wide"; 105 106 // Primary -> managed intent: set unknown sources globally restriction 107 public static final String ACTION_INSTALL_APK_PRIMARY_PROFILE_GLOBAL_RESTRICTION = "com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK_PRIMARY_PROFILE_GLOBAL_RESTRICTION"; 108 // Managed -> primary intent: install primary profile app with global unknown sources 109 // restriction. 110 public static final String ACTION_INSTALL_APK_IN_PRIMARY = "com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK_IN_PRIMARY"; 111 112 // Primary -> managed intent: check if the required cross profile intent filters are set. 113 public static final String ACTION_CHECK_INTENT_FILTERS = 114 "com.android.cts.verifier.managedprovisioning.action.CHECK_INTENT_FILTERS"; 115 116 // Primary -> managed intent: will send a cross profile intent and check if the user sees an 117 // intent picker dialog and can open the apps. 118 public static final String ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG = 119 "com.android.cts.verifier.managedprovisioning.action.TEST_CROSS_PROFILE_INTENTS_DIALOG"; 120 121 // Primary -> managed intent: will send an app link intent and check if the user sees a 122 // dialog and can open the apps. This test is extremely similar to 123 // ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG, but the intent used is a web intent, and there is 124 // some behavior which is specific to web intents. 125 public static final String ACTION_TEST_APP_LINKING_DIALOG = 126 "com.android.cts.verifier.managedprovisioning.action.TEST_APP_LINKING_DIALOG"; 127 128 public static final String ACTION_NOTIFICATION = 129 "com.android.cts.verifier.managedprovisioning.NOTIFICATION"; 130 public static final String ACTION_NOTIFICATION_ON_LOCKSCREEN = 131 "com.android.cts.verifier.managedprovisioning.LOCKSCREEN_NOTIFICATION"; 132 public static final String ACTION_CLEAR_NOTIFICATION = 133 "com.android.cts.verifier.managedprovisioning.CLEAR_NOTIFICATION"; 134 135 // Primary -> managed intent: set a user restriction 136 public static final String ACTION_SET_USER_RESTRICTION = 137 "com.android.cts.verifier.managedprovisioning.BYOD_SET_USER_RESTRICTION"; 138 139 // Primary -> managed intent: reset a user restriction 140 public static final String ACTION_CLEAR_USER_RESTRICTION = 141 "com.android.cts.verifier.managedprovisioning.BYOD_CLEAR_USER_RESTRICTION"; 142 143 // Primary -> managed intent: Start the selection of a work challenge 144 public static final String ACTION_TEST_SELECT_WORK_CHALLENGE = 145 "com.android.cts.verifier.managedprovisioning.TEST_SELECT_WORK_CHALLENGE"; 146 147 // Primary -> managed intent: Start the selection of a work challenge 148 public static final String ACTION_TEST_PATTERN_WORK_CHALLENGE = 149 "com.android.cts.verifier.managedprovisioning.TEST_PATTERN_WORK_CHALLENGE"; 150 151 // Primary -> managed intent: Start the selection of a parent profile password. 152 public static final String ACTION_TEST_PARENT_PROFILE_PASSWORD = 153 "com.android.cts.verifier.managedprovisioning.TEST_PARENT_PROFILE_PASSWORD"; 154 155 // Primary -> managed intent: Start the confirm credentials screen for the managed profile 156 public static final String ACTION_LAUNCH_CONFIRM_WORK_CREDENTIALS = 157 "com.android.cts.verifier.managedprovisioning.LAUNCH_CONFIRM_WORK_CREDENTIALS"; 158 159 public static final String ACTION_SET_ORGANIZATION_INFO = 160 "com.android.cts.verifier.managedprovisioning.TEST_ORGANIZATION_INFO"; 161 162 public static final int RESULT_FAILED = RESULT_FIRST_USER; 163 164 private static final int REQUEST_INSTALL_PACKAGE = 2; 165 private static final int REQUEST_IMAGE_CAPTURE = 3; 166 private static final int REQUEST_VIDEO_CAPTURE_WITH_EXTRA_OUTPUT = 4; 167 private static final int REQUEST_VIDEO_CAPTURE_WITHOUT_EXTRA_OUTPUT = 5; 168 private static final int REQUEST_AUDIO_CAPTURE = 6; 169 private static final int REQUEST_POST_NOTIFICATIONS = 7; 170 171 private static final String ORIGINAL_RESTRICTIONS_NAME = "original restrictions"; 172 173 private static final int NOTIFICATION_ID = 7; 174 private static final String NOTIFICATION_CHANNEL_ID = TAG; 175 176 private static final int EXECUTE_IMAGE_CAPTURE_TEST = 1; 177 private static final int EXECUTE_VIDEO_CAPTURE_WITH_EXTRA_TEST = 2; 178 private static final int EXECUTE_VIDEO_CAPTURE_WITHOUT_EXTRA_TEST = 3; 179 180 private NotificationManager mNotificationManager; 181 private Bundle mOriginalRestrictions; 182 183 private ComponentName mAdminReceiverComponent; 184 private DevicePolicyManager mDevicePolicyManager; 185 186 private Uri mImageUri; 187 private Uri mVideoUri; 188 private File mImageFile; 189 190 private ArrayList<File> mTempFiles = new ArrayList<File>(); 191 192 private Handler mMainThreadHandler; 193 private int mNextNotificationVisibility; 194 showNotification(int visibility)195 private void showNotification(int visibility) { 196 mNextNotificationVisibility = visibility; 197 198 if (hasPostNotificationsPermission()) { 199 showNotificationInner(); 200 } else { 201 requestPostNotificationsPermission(REQUEST_POST_NOTIFICATIONS); 202 } 203 } 204 showNotificationInner()205 private void showNotificationInner() { 206 final Notification notification = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID) 207 .setSmallIcon(R.drawable.icon) 208 .setContentTitle(getString(R.string.provisioning_byod_notification_title)) 209 .setContentText(getString(R.string.provisioning_byod_notification_title)) 210 .setVisibility(mNextNotificationVisibility) 211 .setAutoCancel(true) 212 .setPublicVersion(createPublicVersionNotification()) 213 .build(); 214 mNotificationManager.notify(NOTIFICATION_ID, notification); 215 } 216 createPublicVersionNotification()217 private Notification createPublicVersionNotification() { 218 return new Notification.Builder(this) 219 .setSmallIcon(R.drawable.icon) 220 .setContentTitle(getString(R.string.provisioning_byod_notification_public_title)) 221 .setAutoCancel(true) 222 .build(); 223 } 224 225 @Override onCreate(Bundle savedInstanceState)226 protected void onCreate(Bundle savedInstanceState) { 227 super.onCreate(savedInstanceState); 228 mMainThreadHandler = new Handler(getMainLooper()); 229 if (savedInstanceState != null) { 230 Log.w(TAG, "Restored state"); 231 mOriginalRestrictions = savedInstanceState.getBundle(ORIGINAL_RESTRICTIONS_NAME); 232 } else { 233 mOriginalRestrictions = new Bundle(); 234 } 235 236 mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName()); 237 mDevicePolicyManager = (DevicePolicyManager) getSystemService( 238 Context.DEVICE_POLICY_SERVICE); 239 mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 240 Intent intent = getIntent(); 241 String action = intent.getAction(); 242 Log.d(TAG, "ByodHelperActivity.onCreate: " + action); 243 mNotificationManager.createNotificationChannel(new NotificationChannel( 244 NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID, 245 NotificationManager.IMPORTANCE_DEFAULT)); 246 247 // Queried by CtsVerifier in the primary side using startActivityForResult. 248 if (action.equals(ACTION_QUERY_PROFILE_OWNER)) { 249 Intent response = new Intent(); 250 response.putExtra(EXTRA_PROVISIONED, isProfileOwner()); 251 setResult(RESULT_OK, response); 252 // Request to delete work profile. 253 } else if (action.equals(ACTION_REMOVE_MANAGED_PROFILE)) { 254 if (isProfileOwner() 255 && mDevicePolicyManager.isManagedProfile(mAdminReceiverComponent)) { 256 Log.d(TAG, "Clearing cross profile intents"); 257 mDevicePolicyManager.clearCrossProfileIntentFilters(mAdminReceiverComponent); 258 mDevicePolicyManager.wipeData(0); 259 showToast(R.string.provisioning_byod_profile_deleted); 260 setResult(RESULT_OK); 261 } 262 } else if (action.equals(ACTION_CHECK_DISK_ENCRYPTION)) { 263 final int status = mDevicePolicyManager.getStorageEncryptionStatus(); 264 final Intent response = new Intent(ACTION_DISK_ENCRYPTION_STATUS) 265 .putExtra(EXTRA_ENCRYPTION_STATUS, status); 266 setResult(RESULT_OK, response); 267 } else if (action.equals(ACTION_INSTALL_APK)) { 268 boolean allowNonMarket = intent.getBooleanExtra(EXTRA_ALLOW_NON_MARKET_APPS, false); 269 setRestrictionAndSaveOriginal(DISALLOW_INSTALL_UNKNOWN_SOURCES, !allowNonMarket); 270 startInstallerActivity(intent.getStringExtra(EXTRA_PARAMETER_1)); 271 // Not yet ready to finish - wait until the result comes back 272 return; 273 } else if (action.equals(ACTION_INSTALL_APK_WORK_PROFILE_GLOBAL_RESTRICTION)) { 274 // Save original unknown sources setting to be restored later and clear it for now. 275 setRestrictionAndSaveOriginal(DISALLOW_INSTALL_UNKNOWN_SOURCES, false); 276 boolean allowNonMarketGlobal = intent.getBooleanExtra( 277 EXTRA_ALLOW_NON_MARKET_APPS_DEVICE_WIDE, false); 278 setRestrictionAndSaveOriginal(DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, 279 !allowNonMarketGlobal); 280 startInstallerActivity(intent.getStringExtra(EXTRA_PARAMETER_1)); 281 // Not yet ready to finish - wait until the result comes back 282 return; 283 } else if (action.equals(ACTION_INSTALL_APK_PRIMARY_PROFILE_GLOBAL_RESTRICTION)) { 284 boolean allowNonMarketGlobal = intent.getExtras().getBoolean( 285 EXTRA_ALLOW_NON_MARKET_APPS_DEVICE_WIDE, false); 286 setRestrictionAndSaveOriginal(DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, 287 !allowNonMarketGlobal); 288 setRestrictionAndSaveOriginal(DISALLOW_INSTALL_UNKNOWN_SOURCES, false); 289 Intent installPersonalProfileIntent = new Intent(ACTION_INSTALL_APK_IN_PRIMARY); 290 // Attempt to install an apk in the primary profile 291 startActivityForResult(installPersonalProfileIntent, REQUEST_INSTALL_PACKAGE); 292 return; 293 } else if (action.equals(ACTION_CHECK_INTENT_FILTERS)) { 294 // Queried by CtsVerifier in the primary side using startActivityForResult. 295 final boolean intentFiltersSetForManagedIntents = 296 new IntentFiltersTestHelper(this).checkCrossProfileIntentFilters( 297 IntentFiltersTestHelper.FLAG_INTENTS_FROM_MANAGED); 298 setResult(intentFiltersSetForManagedIntents? RESULT_OK : RESULT_FAILED, null); 299 } else if (action.equals(ACTION_CAPTURE_AND_CHECK_IMAGE)) { 300 if (hasCameraPermission()) { 301 startCaptureImageIntent(); 302 } else { 303 requestCameraPermission(EXECUTE_IMAGE_CAPTURE_TEST); 304 } 305 return; 306 } else if (action.equals(ACTION_CAPTURE_AND_CHECK_VIDEO_WITH_EXTRA_OUTPUT) || 307 action.equals(ACTION_CAPTURE_AND_CHECK_VIDEO_WITHOUT_EXTRA_OUTPUT)) { 308 final int testRequestCode; 309 if (action.equals(ACTION_CAPTURE_AND_CHECK_VIDEO_WITH_EXTRA_OUTPUT)) { 310 testRequestCode = EXECUTE_VIDEO_CAPTURE_WITH_EXTRA_TEST; 311 } else { 312 testRequestCode = EXECUTE_VIDEO_CAPTURE_WITHOUT_EXTRA_TEST; 313 } 314 315 if (hasCameraPermission()) { 316 startCaptureVideoActivity(testRequestCode); 317 } else { 318 requestCameraPermission(testRequestCode); 319 } 320 return; 321 } else if (action.equals(ACTION_CAPTURE_AND_CHECK_AUDIO)) { 322 Intent captureAudioIntent = getCaptureAudioIntent(); 323 if (captureAudioIntent.resolveActivity(getPackageManager()) != null) { 324 startActivityForResult(captureAudioIntent, REQUEST_AUDIO_CAPTURE); 325 } else { 326 Log.e(TAG, "Capture audio intent could not be resolved in managed profile."); 327 showToast(R.string.provisioning_byod_capture_media_error); 328 finish(); 329 } 330 return; 331 } else if (ACTION_KEYGUARD_DISABLED_FEATURES.equals(action)) { 332 final int value = intent.getIntExtra(EXTRA_PARAMETER_1, 333 DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE); 334 mDevicePolicyManager.setKeyguardDisabledFeatures(mAdminReceiverComponent, value); 335 } else if (ACTION_LOCKNOW.equals(action)) { 336 mDevicePolicyManager.lockNow(); 337 setResult(RESULT_OK); 338 } else if (action.equals(ACTION_TEST_NFC_BEAM)) { 339 Intent testNfcBeamIntent = new Intent(this, NfcTestActivity.class); 340 testNfcBeamIntent.putExtras(intent); 341 startActivity(testNfcBeamIntent); 342 finish(); 343 return; 344 } else if (action.equals(ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG)) { 345 sendIntentInsideChooser(new Intent( 346 CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_PERSONAL)); 347 } else if (action.equals(ACTION_TEST_APP_LINKING_DIALOG)) { 348 mDevicePolicyManager.addUserRestriction( 349 DeviceAdminTestReceiver.getReceiverComponentName(), 350 UserManager.ALLOW_PARENT_PROFILE_APP_LINKING); 351 Intent toSend = new Intent(Intent.ACTION_VIEW); 352 toSend.setData(Uri.parse("http://com.android.cts.verifier")); 353 sendIntentInsideChooser(toSend); 354 } else if (action.equals(ACTION_SET_USER_RESTRICTION)) { 355 final String restriction = intent.getStringExtra(EXTRA_PARAMETER_1); 356 if (restriction != null) { 357 mDevicePolicyManager.addUserRestriction( 358 DeviceAdminTestReceiver.getReceiverComponentName(), restriction); 359 } 360 } else if (action.equals(ACTION_CLEAR_USER_RESTRICTION)) { 361 final String restriction = intent.getStringExtra(EXTRA_PARAMETER_1); 362 if (restriction != null) { 363 mDevicePolicyManager.clearUserRestriction( 364 DeviceAdminTestReceiver.getReceiverComponentName(), restriction); 365 } 366 } else if (action.equals(ACTION_NOTIFICATION)) { 367 showNotification(Notification.VISIBILITY_PUBLIC); 368 } else if (ACTION_NOTIFICATION_ON_LOCKSCREEN.equals(action)) { 369 mDevicePolicyManager.lockNow(); 370 showNotification(Notification.VISIBILITY_PRIVATE); 371 } else if (ACTION_CLEAR_NOTIFICATION.equals(action)) { 372 mNotificationManager.cancel(NOTIFICATION_ID); 373 } else if (ACTION_TEST_SELECT_WORK_CHALLENGE.equals(action)) { 374 mDevicePolicyManager.setOrganizationName(mAdminReceiverComponent, getResources() 375 .getString(R.string.provisioning_byod_confirm_work_credentials_header)); 376 startActivity(new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD)); 377 } else if (ACTION_LAUNCH_CONFIRM_WORK_CREDENTIALS.equals(action)) { 378 KeyguardManager keyguardManager = 379 (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); 380 Intent launchIntent = keyguardManager.createConfirmDeviceCredentialIntent(null, null); 381 if (launchIntent != null) { 382 startActivity(launchIntent); 383 } else { 384 showToast(R.string.provisioning_byod_no_secure_lockscreen); 385 } 386 } else if (ACTION_TEST_PATTERN_WORK_CHALLENGE.equals(action)) { 387 startActivity(new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD)); 388 // The remaining steps are manual. 389 } else if (ACTION_SET_ORGANIZATION_INFO.equals(action)) { 390 if(intent.hasExtra(OrganizationInfoTestActivity.EXTRA_ORGANIZATION_NAME)) { 391 final String organizationName = intent 392 .getStringExtra(OrganizationInfoTestActivity.EXTRA_ORGANIZATION_NAME); 393 mDevicePolicyManager.setOrganizationName(mAdminReceiverComponent, organizationName); 394 } 395 } else if (ACTION_TEST_PARENT_PROFILE_PASSWORD.equals(action)) { 396 startActivity(new Intent(DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD)); 397 } 398 // This activity has no UI and is only used to respond to CtsVerifier in the primary side. 399 finish(); 400 } 401 startCaptureVideoActivity(int testRequestCode)402 private void startCaptureVideoActivity(int testRequestCode) { 403 Intent captureVideoIntent = getCaptureVideoIntent(); 404 int videoCaptureRequestId; 405 if (testRequestCode == EXECUTE_VIDEO_CAPTURE_WITH_EXTRA_TEST) { 406 mVideoUri = getTempUri("video.mp4").second; 407 captureVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mVideoUri); 408 videoCaptureRequestId = REQUEST_VIDEO_CAPTURE_WITH_EXTRA_OUTPUT; 409 } else { 410 videoCaptureRequestId = REQUEST_VIDEO_CAPTURE_WITHOUT_EXTRA_OUTPUT; 411 } 412 if (captureVideoIntent.resolveActivity(getPackageManager()) != null) { 413 startActivityForResult(captureVideoIntent, videoCaptureRequestId); 414 } else { 415 Log.e(TAG, "Capture video intent could not be resolved in managed profile."); 416 showToast(R.string.provisioning_byod_capture_media_error); 417 finish(); 418 } 419 } 420 startCaptureImageIntent()421 private void startCaptureImageIntent() { 422 Intent captureImageIntent = getCaptureImageIntent(); 423 Pair<File, Uri> pair = getTempUri("image.jpg"); 424 mImageFile = pair.first; 425 mImageUri = pair.second; 426 captureImageIntent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri); 427 if (captureImageIntent.resolveActivity(getPackageManager()) != null) { 428 startActivityForResult(captureImageIntent, REQUEST_IMAGE_CAPTURE); 429 } else { 430 Log.e(TAG, "Capture image intent could not be resolved in managed profile."); 431 showToast(R.string.provisioning_byod_capture_media_error); 432 finish(); 433 } 434 } 435 startInstallerActivity(String pathToApk)436 private void startInstallerActivity(String pathToApk) { 437 // Start the installer activity until this activity is rendered to workaround a glitch. 438 mMainThreadHandler.post(() -> { 439 final Uri uri; 440 if (pathToApk == null) { 441 // By default we reinstall ourselves, e.g. request to install a non-market app 442 uri = Uri.parse("package:" + getPackageName()); 443 } else { 444 uri = FileProvider.getUriForFile( 445 this, Utils.FILE_PROVIDER_AUTHORITY, new File(pathToApk)); 446 } 447 final Intent installIntent = new Intent(Intent.ACTION_INSTALL_PACKAGE) 448 .setData(uri) 449 .putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true) 450 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 451 .putExtra(Intent.EXTRA_RETURN_RESULT, true); 452 startActivityForResult(installIntent, REQUEST_INSTALL_PACKAGE); 453 }); 454 } 455 456 @Override onSaveInstanceState(final Bundle savedState)457 protected void onSaveInstanceState(final Bundle savedState) { 458 super.onSaveInstanceState(savedState); 459 460 savedState.putBundle(ORIGINAL_RESTRICTIONS_NAME, mOriginalRestrictions); 461 } 462 463 @Override onActivityResult(int requestCode, int resultCode, Intent data)464 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 465 switch (requestCode) { 466 case REQUEST_INSTALL_PACKAGE: { 467 Log.w(TAG, "Received REQUEST_INSTALL_PACKAGE, resultCode = " + resultCode); 468 // Restore original settings for restrictions being changed before installs. 469 restoreOriginalRestriction(DISALLOW_INSTALL_UNKNOWN_SOURCES); 470 restoreOriginalRestriction(DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY); 471 finish(); 472 break; 473 } 474 case REQUEST_IMAGE_CAPTURE: { 475 if (resultCode == RESULT_OK) { 476 ByodPresentMediaDialog.newImageInstance(mImageFile) 477 .show(getFragmentManager(), "ViewImageDialogFragment"); 478 } else { 479 // Failed capturing image. 480 finish(); 481 } 482 break; 483 } 484 case REQUEST_VIDEO_CAPTURE_WITH_EXTRA_OUTPUT: { 485 if (resultCode == RESULT_OK) { 486 ByodPresentMediaDialog.newVideoInstance(mVideoUri) 487 .show(getFragmentManager(), "PlayVideoDialogFragment"); 488 } else { 489 // Failed capturing video. 490 finish(); 491 } 492 break; 493 } 494 case REQUEST_VIDEO_CAPTURE_WITHOUT_EXTRA_OUTPUT: { 495 if (resultCode == RESULT_OK) { 496 ByodPresentMediaDialog.newVideoInstance(data.getData()) 497 .show(getFragmentManager(), "PlayVideoDialogFragment"); 498 } else { 499 // Failed capturing video. 500 finish(); 501 } 502 break; 503 } 504 case REQUEST_AUDIO_CAPTURE: { 505 if (resultCode == RESULT_OK) { 506 ByodPresentMediaDialog.newAudioInstance(data.getData()) 507 .show(getFragmentManager(), "PlayAudioDialogFragment"); 508 } else { 509 // Failed capturing audio. 510 finish(); 511 } 512 break; 513 } 514 default: { 515 super.onActivityResult(requestCode, resultCode, data); 516 break; 517 } 518 } 519 } 520 521 @Override onDestroy()522 protected void onDestroy() { 523 cleanUpTempUris(); 524 super.onDestroy(); 525 } 526 getCaptureImageIntent()527 public static Intent getCaptureImageIntent() { 528 return new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 529 } 530 getCaptureVideoIntent()531 public static Intent getCaptureVideoIntent() { 532 return new Intent(MediaStore.ACTION_VIDEO_CAPTURE); 533 } 534 getCaptureAudioIntent()535 public static Intent getCaptureAudioIntent() { 536 return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION); 537 } 538 createLockIntent()539 public static Intent createLockIntent() { 540 return new Intent(ACTION_LOCKNOW); 541 } 542 getTempUri(String fileName)543 private Pair<File, Uri> getTempUri(String fileName) { 544 final File file = new File(getFilesDir() + File.separator + "images" 545 + File.separator + fileName); 546 file.getParentFile().mkdirs(); //if the folder doesn't exists it is created 547 mTempFiles.add(file); 548 return new Pair<>(file, FileProvider.getUriForFile( 549 this, Utils.FILE_PROVIDER_AUTHORITY, file)); 550 } 551 cleanUpTempUris()552 private void cleanUpTempUris() { 553 for (File file : mTempFiles) { 554 file.delete(); 555 } 556 } 557 isProfileOwner()558 private boolean isProfileOwner() { 559 return mDevicePolicyManager.isAdminActive(mAdminReceiverComponent) && 560 mDevicePolicyManager.isProfileOwnerApp(mAdminReceiverComponent.getPackageName()); 561 } 562 isRestrictionSet(String restriction)563 private boolean isRestrictionSet(String restriction) { 564 Bundle restrictions = mDevicePolicyManager.getUserRestrictions(mAdminReceiverComponent); 565 // This defaults to false if there is no value already there. If a restriction was true, 566 // the restriction would already be set. 567 return restrictions.getBoolean(restriction, false); 568 } 569 setRestriction(String restriction, boolean enabled)570 private void setRestriction(String restriction, boolean enabled) { 571 if (enabled) { 572 mDevicePolicyManager.addUserRestriction(mAdminReceiverComponent, restriction); 573 } else { 574 mDevicePolicyManager.clearUserRestriction(mAdminReceiverComponent, restriction); 575 } 576 } 577 setRestrictionAndSaveOriginal(String restriction, boolean enabled)578 private void setRestrictionAndSaveOriginal(String restriction, boolean enabled) { 579 // Saves original restriction values in mOriginalRestrictions before changing its value. 580 boolean original = isRestrictionSet(restriction); 581 if (enabled != original) { 582 setRestriction(restriction, enabled); 583 mOriginalRestrictions.putBoolean(restriction, original); 584 } 585 } 586 restoreOriginalRestriction(String restriction)587 public void restoreOriginalRestriction(String restriction) { 588 if (mOriginalRestrictions.containsKey(restriction)) { 589 setRestriction(restriction, mOriginalRestrictions.getBoolean(restriction)); 590 mOriginalRestrictions.remove(restriction); 591 } 592 } 593 hasCameraPermission()594 private boolean hasCameraPermission() { 595 return ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) 596 == PackageManager.PERMISSION_GRANTED; 597 } 598 requestCameraPermission(int requestCode)599 private void requestCameraPermission(int requestCode) { 600 ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.CAMERA}, 601 requestCode); 602 } 603 hasLocationPermission()604 private boolean hasLocationPermission() { 605 return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) 606 == PackageManager.PERMISSION_GRANTED; 607 } 608 requestLocationPermission(int requestCode)609 private void requestLocationPermission(int requestCode) { 610 ActivityCompat.requestPermissions(this, 611 new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 612 requestCode); 613 } 614 hasPostNotificationsPermission()615 private boolean hasPostNotificationsPermission() { 616 return ContextCompat.checkSelfPermission(this, POST_NOTIFICATIONS) 617 == PackageManager.PERMISSION_GRANTED; 618 } 619 requestPostNotificationsPermission(int requestCode)620 private void requestPostNotificationsPermission(int requestCode) { 621 ActivityCompat.requestPermissions(this, 622 new String[]{Manifest.permission.POST_NOTIFICATIONS}, 623 requestCode); 624 } 625 626 /** 627 * Launch the right test based on the request code, after validating the right permission 628 * has been granted. 629 */ 630 @Override onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grants)631 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 632 @NonNull int[] grants) { 633 // Test that the right permission was granted. 634 switch(requestCode) { 635 case EXECUTE_IMAGE_CAPTURE_TEST: 636 case EXECUTE_VIDEO_CAPTURE_WITH_EXTRA_TEST: 637 case EXECUTE_VIDEO_CAPTURE_WITHOUT_EXTRA_TEST: 638 if (!permissions[0].equals(android.Manifest.permission.CAMERA) 639 || grants[0] != PackageManager.PERMISSION_GRANTED) { 640 Log.e(TAG, "The test needs camera permission."); 641 showToast(R.string.provisioning_byod_capture_media_error); 642 finish(); 643 return; 644 } 645 break; 646 case REQUEST_POST_NOTIFICATIONS: 647 if (!permissions[0].equals(POST_NOTIFICATIONS) 648 || grants[0] != PackageManager.PERMISSION_GRANTED) { 649 Log.e(TAG, "The test needs notifications permission."); 650 finish(); 651 return; 652 } 653 break; 654 } 655 656 // Execute the right test. 657 switch (requestCode) { 658 case EXECUTE_IMAGE_CAPTURE_TEST: 659 startCaptureImageIntent(); 660 break; 661 case EXECUTE_VIDEO_CAPTURE_WITH_EXTRA_TEST: 662 case EXECUTE_VIDEO_CAPTURE_WITHOUT_EXTRA_TEST: 663 startCaptureVideoActivity(requestCode); 664 break; 665 case REQUEST_POST_NOTIFICATIONS: 666 showNotificationInner(); 667 break; 668 default: 669 Log.e(TAG, "Unknown action."); 670 finish(); 671 } 672 } 673 sendIntentInsideChooser(Intent toSend)674 private void sendIntentInsideChooser(Intent toSend) { 675 toSend.putExtra(CrossProfileTestActivity.EXTRA_STARTED_FROM_WORK, true); 676 Intent chooser = Intent.createChooser(toSend, 677 getResources().getString(R.string.provisioning_cross_profile_chooser)); 678 startActivity(chooser); 679 } 680 681 @Override onDialogClose()682 public void onDialogClose() { 683 finish(); 684 } 685 showToast(int messageId)686 private void showToast(int messageId) { 687 String message = getString(messageId); 688 Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); 689 } 690 } 691