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