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