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 Log.d(TAG, "Clearing cross profile intents"); 256 mDevicePolicyManager.clearCrossProfileIntentFilters(mAdminReceiverComponent); 257 mDevicePolicyManager.wipeData(0); 258 showToast(R.string.provisioning_byod_profile_deleted); 259 } 260 } else if (action.equals(ACTION_CHECK_DISK_ENCRYPTION)) { 261 final int status = mDevicePolicyManager.getStorageEncryptionStatus(); 262 final Intent response = new Intent(ACTION_DISK_ENCRYPTION_STATUS) 263 .putExtra(EXTRA_ENCRYPTION_STATUS, status); 264 setResult(RESULT_OK, response); 265 } else if (action.equals(ACTION_INSTALL_APK)) { 266 boolean allowNonMarket = intent.getBooleanExtra(EXTRA_ALLOW_NON_MARKET_APPS, false); 267 setRestrictionAndSaveOriginal(DISALLOW_INSTALL_UNKNOWN_SOURCES, !allowNonMarket); 268 startInstallerActivity(intent.getStringExtra(EXTRA_PARAMETER_1)); 269 // Not yet ready to finish - wait until the result comes back 270 return; 271 } else if (action.equals(ACTION_INSTALL_APK_WORK_PROFILE_GLOBAL_RESTRICTION)) { 272 // Save original unknown sources setting to be restored later and clear it for now. 273 setRestrictionAndSaveOriginal(DISALLOW_INSTALL_UNKNOWN_SOURCES, false); 274 boolean allowNonMarketGlobal = intent.getBooleanExtra( 275 EXTRA_ALLOW_NON_MARKET_APPS_DEVICE_WIDE, false); 276 setRestrictionAndSaveOriginal(DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, 277 !allowNonMarketGlobal); 278 startInstallerActivity(intent.getStringExtra(EXTRA_PARAMETER_1)); 279 // Not yet ready to finish - wait until the result comes back 280 return; 281 } else if (action.equals(ACTION_INSTALL_APK_PRIMARY_PROFILE_GLOBAL_RESTRICTION)) { 282 boolean allowNonMarketGlobal = intent.getExtras().getBoolean( 283 EXTRA_ALLOW_NON_MARKET_APPS_DEVICE_WIDE, false); 284 setRestrictionAndSaveOriginal(DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, 285 !allowNonMarketGlobal); 286 setRestrictionAndSaveOriginal(DISALLOW_INSTALL_UNKNOWN_SOURCES, false); 287 Intent installPersonalProfileIntent = new Intent(ACTION_INSTALL_APK_IN_PRIMARY); 288 // Attempt to install an apk in the primary profile 289 startActivityForResult(installPersonalProfileIntent, REQUEST_INSTALL_PACKAGE); 290 return; 291 } else if (action.equals(ACTION_CHECK_INTENT_FILTERS)) { 292 // Queried by CtsVerifier in the primary side using startActivityForResult. 293 final boolean intentFiltersSetForManagedIntents = 294 new IntentFiltersTestHelper(this).checkCrossProfileIntentFilters( 295 IntentFiltersTestHelper.FLAG_INTENTS_FROM_MANAGED); 296 setResult(intentFiltersSetForManagedIntents? RESULT_OK : RESULT_FAILED, null); 297 } else if (action.equals(ACTION_CAPTURE_AND_CHECK_IMAGE)) { 298 if (hasCameraPermission()) { 299 startCaptureImageIntent(); 300 } else { 301 requestCameraPermission(EXECUTE_IMAGE_CAPTURE_TEST); 302 } 303 return; 304 } else if (action.equals(ACTION_CAPTURE_AND_CHECK_VIDEO_WITH_EXTRA_OUTPUT) || 305 action.equals(ACTION_CAPTURE_AND_CHECK_VIDEO_WITHOUT_EXTRA_OUTPUT)) { 306 final int testRequestCode; 307 if (action.equals(ACTION_CAPTURE_AND_CHECK_VIDEO_WITH_EXTRA_OUTPUT)) { 308 testRequestCode = EXECUTE_VIDEO_CAPTURE_WITH_EXTRA_TEST; 309 } else { 310 testRequestCode = EXECUTE_VIDEO_CAPTURE_WITHOUT_EXTRA_TEST; 311 } 312 313 if (hasCameraPermission()) { 314 startCaptureVideoActivity(testRequestCode); 315 } else { 316 requestCameraPermission(testRequestCode); 317 } 318 return; 319 } else if (action.equals(ACTION_CAPTURE_AND_CHECK_AUDIO)) { 320 Intent captureAudioIntent = getCaptureAudioIntent(); 321 if (captureAudioIntent.resolveActivity(getPackageManager()) != null) { 322 startActivityForResult(captureAudioIntent, REQUEST_AUDIO_CAPTURE); 323 } else { 324 Log.e(TAG, "Capture audio intent could not be resolved in managed profile."); 325 showToast(R.string.provisioning_byod_capture_media_error); 326 finish(); 327 } 328 return; 329 } else if (ACTION_KEYGUARD_DISABLED_FEATURES.equals(action)) { 330 final int value = intent.getIntExtra(EXTRA_PARAMETER_1, 331 DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE); 332 mDevicePolicyManager.setKeyguardDisabledFeatures(mAdminReceiverComponent, value); 333 } else if (ACTION_LOCKNOW.equals(action)) { 334 mDevicePolicyManager.lockNow(); 335 setResult(RESULT_OK); 336 } else if (action.equals(ACTION_TEST_NFC_BEAM)) { 337 Intent testNfcBeamIntent = new Intent(this, NfcTestActivity.class); 338 testNfcBeamIntent.putExtras(intent); 339 startActivity(testNfcBeamIntent); 340 finish(); 341 return; 342 } else if (action.equals(ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG)) { 343 sendIntentInsideChooser(new Intent( 344 CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_PERSONAL)); 345 } else if (action.equals(ACTION_TEST_APP_LINKING_DIALOG)) { 346 mDevicePolicyManager.addUserRestriction( 347 DeviceAdminTestReceiver.getReceiverComponentName(), 348 UserManager.ALLOW_PARENT_PROFILE_APP_LINKING); 349 Intent toSend = new Intent(Intent.ACTION_VIEW); 350 toSend.setData(Uri.parse("http://com.android.cts.verifier")); 351 sendIntentInsideChooser(toSend); 352 } else if (action.equals(ACTION_SET_USER_RESTRICTION)) { 353 final String restriction = intent.getStringExtra(EXTRA_PARAMETER_1); 354 if (restriction != null) { 355 mDevicePolicyManager.addUserRestriction( 356 DeviceAdminTestReceiver.getReceiverComponentName(), restriction); 357 } 358 } else if (action.equals(ACTION_CLEAR_USER_RESTRICTION)) { 359 final String restriction = intent.getStringExtra(EXTRA_PARAMETER_1); 360 if (restriction != null) { 361 mDevicePolicyManager.clearUserRestriction( 362 DeviceAdminTestReceiver.getReceiverComponentName(), restriction); 363 } 364 } else if (action.equals(ACTION_NOTIFICATION)) { 365 showNotification(Notification.VISIBILITY_PUBLIC); 366 } else if (ACTION_NOTIFICATION_ON_LOCKSCREEN.equals(action)) { 367 mDevicePolicyManager.lockNow(); 368 showNotification(Notification.VISIBILITY_PRIVATE); 369 } else if (ACTION_CLEAR_NOTIFICATION.equals(action)) { 370 mNotificationManager.cancel(NOTIFICATION_ID); 371 } else if (ACTION_TEST_SELECT_WORK_CHALLENGE.equals(action)) { 372 mDevicePolicyManager.setOrganizationName(mAdminReceiverComponent, getResources() 373 .getString(R.string.provisioning_byod_confirm_work_credentials_header)); 374 startActivity(new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD)); 375 } else if (ACTION_LAUNCH_CONFIRM_WORK_CREDENTIALS.equals(action)) { 376 KeyguardManager keyguardManager = 377 (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); 378 Intent launchIntent = keyguardManager.createConfirmDeviceCredentialIntent(null, null); 379 if (launchIntent != null) { 380 startActivity(launchIntent); 381 } else { 382 showToast(R.string.provisioning_byod_no_secure_lockscreen); 383 } 384 } else if (ACTION_TEST_PATTERN_WORK_CHALLENGE.equals(action)) { 385 startActivity(new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD)); 386 // The remaining steps are manual. 387 } else if (ACTION_SET_ORGANIZATION_INFO.equals(action)) { 388 if(intent.hasExtra(OrganizationInfoTestActivity.EXTRA_ORGANIZATION_NAME)) { 389 final String organizationName = intent 390 .getStringExtra(OrganizationInfoTestActivity.EXTRA_ORGANIZATION_NAME); 391 mDevicePolicyManager.setOrganizationName(mAdminReceiverComponent, organizationName); 392 } 393 } else if (ACTION_TEST_PARENT_PROFILE_PASSWORD.equals(action)) { 394 startActivity(new Intent(DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD)); 395 } 396 // This activity has no UI and is only used to respond to CtsVerifier in the primary side. 397 finish(); 398 } 399 startCaptureVideoActivity(int testRequestCode)400 private void startCaptureVideoActivity(int testRequestCode) { 401 Intent captureVideoIntent = getCaptureVideoIntent(); 402 int videoCaptureRequestId; 403 if (testRequestCode == EXECUTE_VIDEO_CAPTURE_WITH_EXTRA_TEST) { 404 mVideoUri = getTempUri("video.mp4").second; 405 captureVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mVideoUri); 406 videoCaptureRequestId = REQUEST_VIDEO_CAPTURE_WITH_EXTRA_OUTPUT; 407 } else { 408 videoCaptureRequestId = REQUEST_VIDEO_CAPTURE_WITHOUT_EXTRA_OUTPUT; 409 } 410 if (captureVideoIntent.resolveActivity(getPackageManager()) != null) { 411 startActivityForResult(captureVideoIntent, videoCaptureRequestId); 412 } else { 413 Log.e(TAG, "Capture video intent could not be resolved in managed profile."); 414 showToast(R.string.provisioning_byod_capture_media_error); 415 finish(); 416 } 417 } 418 startCaptureImageIntent()419 private void startCaptureImageIntent() { 420 Intent captureImageIntent = getCaptureImageIntent(); 421 Pair<File, Uri> pair = getTempUri("image.jpg"); 422 mImageFile = pair.first; 423 mImageUri = pair.second; 424 captureImageIntent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri); 425 if (captureImageIntent.resolveActivity(getPackageManager()) != null) { 426 startActivityForResult(captureImageIntent, REQUEST_IMAGE_CAPTURE); 427 } else { 428 Log.e(TAG, "Capture image intent could not be resolved in managed profile."); 429 showToast(R.string.provisioning_byod_capture_media_error); 430 finish(); 431 } 432 } 433 startInstallerActivity(String pathToApk)434 private void startInstallerActivity(String pathToApk) { 435 // Start the installer activity until this activity is rendered to workaround a glitch. 436 mMainThreadHandler.post(() -> { 437 final Uri uri; 438 if (pathToApk == null) { 439 // By default we reinstall ourselves, e.g. request to install a non-market app 440 uri = Uri.parse("package:" + getPackageName()); 441 } else { 442 uri = FileProvider.getUriForFile( 443 this, Utils.FILE_PROVIDER_AUTHORITY, new File(pathToApk)); 444 } 445 final Intent installIntent = new Intent(Intent.ACTION_INSTALL_PACKAGE) 446 .setData(uri) 447 .putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true) 448 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 449 .putExtra(Intent.EXTRA_RETURN_RESULT, true); 450 startActivityForResult(installIntent, REQUEST_INSTALL_PACKAGE); 451 }); 452 } 453 454 @Override onSaveInstanceState(final Bundle savedState)455 protected void onSaveInstanceState(final Bundle savedState) { 456 super.onSaveInstanceState(savedState); 457 458 savedState.putBundle(ORIGINAL_RESTRICTIONS_NAME, mOriginalRestrictions); 459 } 460 461 @Override onActivityResult(int requestCode, int resultCode, Intent data)462 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 463 switch (requestCode) { 464 case REQUEST_INSTALL_PACKAGE: { 465 Log.w(TAG, "Received REQUEST_INSTALL_PACKAGE, resultCode = " + resultCode); 466 // Restore original settings for restrictions being changed before installs. 467 restoreOriginalRestriction(DISALLOW_INSTALL_UNKNOWN_SOURCES); 468 restoreOriginalRestriction(DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY); 469 finish(); 470 break; 471 } 472 case REQUEST_IMAGE_CAPTURE: { 473 if (resultCode == RESULT_OK) { 474 ByodPresentMediaDialog.newImageInstance(mImageFile) 475 .show(getFragmentManager(), "ViewImageDialogFragment"); 476 } else { 477 // Failed capturing image. 478 finish(); 479 } 480 break; 481 } 482 case REQUEST_VIDEO_CAPTURE_WITH_EXTRA_OUTPUT: { 483 if (resultCode == RESULT_OK) { 484 ByodPresentMediaDialog.newVideoInstance(mVideoUri) 485 .show(getFragmentManager(), "PlayVideoDialogFragment"); 486 } else { 487 // Failed capturing video. 488 finish(); 489 } 490 break; 491 } 492 case REQUEST_VIDEO_CAPTURE_WITHOUT_EXTRA_OUTPUT: { 493 if (resultCode == RESULT_OK) { 494 ByodPresentMediaDialog.newVideoInstance(data.getData()) 495 .show(getFragmentManager(), "PlayVideoDialogFragment"); 496 } else { 497 // Failed capturing video. 498 finish(); 499 } 500 break; 501 } 502 case REQUEST_AUDIO_CAPTURE: { 503 if (resultCode == RESULT_OK) { 504 ByodPresentMediaDialog.newAudioInstance(data.getData()) 505 .show(getFragmentManager(), "PlayAudioDialogFragment"); 506 } else { 507 // Failed capturing audio. 508 finish(); 509 } 510 break; 511 } 512 default: { 513 super.onActivityResult(requestCode, resultCode, data); 514 break; 515 } 516 } 517 } 518 519 @Override onDestroy()520 protected void onDestroy() { 521 cleanUpTempUris(); 522 super.onDestroy(); 523 } 524 getCaptureImageIntent()525 public static Intent getCaptureImageIntent() { 526 return new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 527 } 528 getCaptureVideoIntent()529 public static Intent getCaptureVideoIntent() { 530 return new Intent(MediaStore.ACTION_VIDEO_CAPTURE); 531 } 532 getCaptureAudioIntent()533 public static Intent getCaptureAudioIntent() { 534 return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION); 535 } 536 createLockIntent()537 public static Intent createLockIntent() { 538 return new Intent(ACTION_LOCKNOW); 539 } 540 getTempUri(String fileName)541 private Pair<File, Uri> getTempUri(String fileName) { 542 final File file = new File(getFilesDir() + File.separator + "images" 543 + File.separator + fileName); 544 file.getParentFile().mkdirs(); //if the folder doesn't exists it is created 545 mTempFiles.add(file); 546 return new Pair<>(file, FileProvider.getUriForFile( 547 this, Utils.FILE_PROVIDER_AUTHORITY, file)); 548 } 549 cleanUpTempUris()550 private void cleanUpTempUris() { 551 for (File file : mTempFiles) { 552 file.delete(); 553 } 554 } 555 isProfileOwner()556 private boolean isProfileOwner() { 557 return mDevicePolicyManager.isAdminActive(mAdminReceiverComponent) && 558 mDevicePolicyManager.isProfileOwnerApp(mAdminReceiverComponent.getPackageName()); 559 } 560 isRestrictionSet(String restriction)561 private boolean isRestrictionSet(String restriction) { 562 Bundle restrictions = mDevicePolicyManager.getUserRestrictions(mAdminReceiverComponent); 563 // This defaults to false if there is no value already there. If a restriction was true, 564 // the restriction would already be set. 565 return restrictions.getBoolean(restriction, false); 566 } 567 setRestriction(String restriction, boolean enabled)568 private void setRestriction(String restriction, boolean enabled) { 569 if (enabled) { 570 mDevicePolicyManager.addUserRestriction(mAdminReceiverComponent, restriction); 571 } else { 572 mDevicePolicyManager.clearUserRestriction(mAdminReceiverComponent, restriction); 573 } 574 } 575 setRestrictionAndSaveOriginal(String restriction, boolean enabled)576 private void setRestrictionAndSaveOriginal(String restriction, boolean enabled) { 577 // Saves original restriction values in mOriginalRestrictions before changing its value. 578 boolean original = isRestrictionSet(restriction); 579 if (enabled != original) { 580 setRestriction(restriction, enabled); 581 mOriginalRestrictions.putBoolean(restriction, original); 582 } 583 } 584 restoreOriginalRestriction(String restriction)585 public void restoreOriginalRestriction(String restriction) { 586 if (mOriginalRestrictions.containsKey(restriction)) { 587 setRestriction(restriction, mOriginalRestrictions.getBoolean(restriction)); 588 mOriginalRestrictions.remove(restriction); 589 } 590 } 591 hasCameraPermission()592 private boolean hasCameraPermission() { 593 return ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) 594 == PackageManager.PERMISSION_GRANTED; 595 } 596 requestCameraPermission(int requestCode)597 private void requestCameraPermission(int requestCode) { 598 ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.CAMERA}, 599 requestCode); 600 } 601 hasLocationPermission()602 private boolean hasLocationPermission() { 603 return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) 604 == PackageManager.PERMISSION_GRANTED; 605 } 606 requestLocationPermission(int requestCode)607 private void requestLocationPermission(int requestCode) { 608 ActivityCompat.requestPermissions(this, 609 new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 610 requestCode); 611 } 612 hasPostNotificationsPermission()613 private boolean hasPostNotificationsPermission() { 614 return ContextCompat.checkSelfPermission(this, POST_NOTIFICATIONS) 615 == PackageManager.PERMISSION_GRANTED; 616 } 617 requestPostNotificationsPermission(int requestCode)618 private void requestPostNotificationsPermission(int requestCode) { 619 ActivityCompat.requestPermissions(this, 620 new String[]{Manifest.permission.POST_NOTIFICATIONS}, 621 requestCode); 622 } 623 624 /** 625 * Launch the right test based on the request code, after validating the right permission 626 * has been granted. 627 */ 628 @Override onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grants)629 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 630 @NonNull int[] grants) { 631 // Test that the right permission was granted. 632 switch(requestCode) { 633 case EXECUTE_IMAGE_CAPTURE_TEST: 634 case EXECUTE_VIDEO_CAPTURE_WITH_EXTRA_TEST: 635 case EXECUTE_VIDEO_CAPTURE_WITHOUT_EXTRA_TEST: 636 if (!permissions[0].equals(android.Manifest.permission.CAMERA) 637 || grants[0] != PackageManager.PERMISSION_GRANTED) { 638 Log.e(TAG, "The test needs camera permission."); 639 showToast(R.string.provisioning_byod_capture_media_error); 640 finish(); 641 return; 642 } 643 break; 644 case REQUEST_POST_NOTIFICATIONS: 645 if (!permissions[0].equals(POST_NOTIFICATIONS) 646 || grants[0] != PackageManager.PERMISSION_GRANTED) { 647 Log.e(TAG, "The test needs notifications permission."); 648 finish(); 649 return; 650 } 651 break; 652 } 653 654 // Execute the right test. 655 switch (requestCode) { 656 case EXECUTE_IMAGE_CAPTURE_TEST: 657 startCaptureImageIntent(); 658 break; 659 case EXECUTE_VIDEO_CAPTURE_WITH_EXTRA_TEST: 660 case EXECUTE_VIDEO_CAPTURE_WITHOUT_EXTRA_TEST: 661 startCaptureVideoActivity(requestCode); 662 break; 663 case REQUEST_POST_NOTIFICATIONS: 664 showNotificationInner(); 665 break; 666 default: 667 Log.e(TAG, "Unknown action."); 668 finish(); 669 } 670 } 671 sendIntentInsideChooser(Intent toSend)672 private void sendIntentInsideChooser(Intent toSend) { 673 toSend.putExtra(CrossProfileTestActivity.EXTRA_STARTED_FROM_WORK, true); 674 Intent chooser = Intent.createChooser(toSend, 675 getResources().getString(R.string.provisioning_cross_profile_chooser)); 676 startActivity(chooser); 677 } 678 679 @Override onDialogClose()680 public void onDialogClose() { 681 finish(); 682 } 683 showToast(int messageId)684 private void showToast(int messageId) { 685 String message = getString(messageId); 686 Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); 687 } 688 } 689