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