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