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