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