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