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