Home | History | Annotate | Download | only in provisioning
      1 /*
      2  * Copyright 2016, 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.managedprovisioning.provisioning;
     18 
     19 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS;
     20 
     21 import android.Manifest;
     22 import android.Manifest.permission;
     23 import android.app.Activity;
     24 import android.app.DialogFragment;
     25 import android.app.admin.DevicePolicyManager;
     26 import android.content.ComponentName;
     27 import android.content.Intent;
     28 import android.content.pm.PackageManager;
     29 import android.content.pm.ResolveInfo;
     30 import android.os.Bundle;
     31 import android.support.annotation.VisibleForTesting;
     32 import android.view.accessibility.AccessibilityEvent;
     33 import android.widget.TextView;
     34 
     35 import com.android.managedprovisioning.R;
     36 import com.android.managedprovisioning.common.DialogBuilder;
     37 import com.android.managedprovisioning.common.ProvisionLogger;
     38 import com.android.managedprovisioning.common.SetupGlifLayoutActivity;
     39 import com.android.managedprovisioning.common.SimpleDialog;
     40 import com.android.managedprovisioning.common.Utils;
     41 import com.android.managedprovisioning.model.CustomizationParams;
     42 import com.android.managedprovisioning.model.ProvisioningParams;
     43 import java.util.List;
     44 
     45 /**
     46  * Progress activity shown whilst provisioning is ongoing.
     47  *
     48  * <p>This activity registers for updates of the provisioning process from the
     49  * {@link ProvisioningManager}. It shows progress updates as provisioning progresses and handles
     50  * showing of cancel and error dialogs.</p>
     51  */
     52 public class ProvisioningActivity extends SetupGlifLayoutActivity
     53         implements SimpleDialog.SimpleDialogListener, ProvisioningManagerCallback {
     54 
     55     private static final String KEY_PROVISIONING_STARTED = "ProvisioningStarted";
     56 
     57     private static final String ERROR_DIALOG_OK = "ErrorDialogOk";
     58     private static final String ERROR_DIALOG_RESET = "ErrorDialogReset";
     59     private static final String CANCEL_PROVISIONING_DIALOG_OK = "CancelProvisioningDialogOk";
     60     private static final String CANCEL_PROVISIONING_DIALOG_RESET = "CancelProvisioningDialogReset";
     61 
     62     private ProvisioningParams mParams;
     63     private ProvisioningManager mProvisioningManager;
     64 
     65     public ProvisioningActivity() {
     66         this(null, new Utils());
     67     }
     68 
     69     @VisibleForTesting
     70     public ProvisioningActivity(ProvisioningManager provisioningManager, Utils utils) {
     71         super(utils);
     72         mProvisioningManager = provisioningManager;
     73     }
     74 
     75     // Lazily initialize ProvisioningManager, since we can't call in ProvisioningManager.getInstance
     76     // in constructor as base context is not available in constructor
     77     private ProvisioningManager getProvisioningManager() {
     78         if (mProvisioningManager == null) {
     79             mProvisioningManager = ProvisioningManager.getInstance(this);
     80         }
     81         return mProvisioningManager;
     82     }
     83 
     84     @Override
     85     protected void onCreate(Bundle savedInstanceState) {
     86         super.onCreate(savedInstanceState);
     87         mParams = getIntent().getParcelableExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS);
     88         initializeUi(mParams);
     89 
     90         if (savedInstanceState == null
     91                 || !savedInstanceState.getBoolean(KEY_PROVISIONING_STARTED)) {
     92             getProvisioningManager().maybeStartProvisioning(mParams);
     93         }
     94     }
     95 
     96     @Override
     97     protected void onSaveInstanceState(Bundle outState) {
     98         super.onSaveInstanceState(outState);
     99         outState.putBoolean(KEY_PROVISIONING_STARTED, true);
    100     }
    101 
    102     @Override
    103     protected void onResume() {
    104         super.onResume();
    105         if (!isAnyDialogAdded()) {
    106             getProvisioningManager().registerListener(this);
    107         }
    108     }
    109 
    110     private boolean isAnyDialogAdded() {
    111         return isDialogAdded(ERROR_DIALOG_OK)
    112                 || isDialogAdded(ERROR_DIALOG_RESET)
    113                 || isDialogAdded(CANCEL_PROVISIONING_DIALOG_OK)
    114                 || isDialogAdded(CANCEL_PROVISIONING_DIALOG_RESET);
    115     }
    116 
    117     @Override
    118     public void onPause() {
    119         getProvisioningManager().unregisterListener(this);
    120         super.onPause();
    121     }
    122 
    123     @Override
    124     public void onBackPressed() {
    125         // if EXTRA_PROVISIONING_SKIP_USER_CONSENT is specified, don't allow user to cancel
    126         if (mParams.skipUserConsent) {
    127             return;
    128         }
    129 
    130         showCancelProvisioningDialog();
    131     }
    132 
    133     @Override
    134     public void preFinalizationCompleted() {
    135         ProvisionLogger.logi("ProvisioningActivity pre-finalization completed");
    136         setResult(Activity.RESULT_OK);
    137         maybeLaunchNfcUserSetupCompleteIntent();
    138         finish();
    139     }
    140 
    141     private void maybeLaunchNfcUserSetupCompleteIntent() {
    142         if (mParams != null && mParams.isNfc) {
    143             // Start SetupWizard to complete the intent.
    144             final Intent intent = new Intent(DevicePolicyManager.ACTION_STATE_USER_SETUP_COMPLETE)
    145                     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    146             final PackageManager pm = getPackageManager();
    147             List<ResolveInfo> ris = pm.queryIntentActivities(intent, 0);
    148 
    149             // Look for the first legitimate component protected by the permission
    150             ComponentName targetComponent = null;
    151             for (ResolveInfo ri : ris) {
    152                 if (ri.activityInfo == null) {
    153                     continue;
    154                 }
    155                 if (!permission.BIND_DEVICE_ADMIN.equals(ri.activityInfo.permission)) {
    156                     ProvisionLogger.loge("Component " + ri.activityInfo.getComponentName()
    157                             + " is not protected by " + permission.BIND_DEVICE_ADMIN);
    158                 } else if (pm.checkPermission(permission.DISPATCH_PROVISIONING_MESSAGE,
    159                         ri.activityInfo.packageName) != PackageManager.PERMISSION_GRANTED) {
    160                     ProvisionLogger.loge("Package " + ri.activityInfo.packageName
    161                             + " does not have " + permission.DISPATCH_PROVISIONING_MESSAGE);
    162                 } else {
    163                     targetComponent = ri.activityInfo.getComponentName();
    164                     break;
    165                 }
    166             }
    167 
    168             if (targetComponent == null) {
    169                 ProvisionLogger.logw("No activity accepts intent ACTION_STATE_USER_SETUP_COMPLETE");
    170                 return;
    171             }
    172 
    173             intent.setComponent(targetComponent);
    174             startActivity(intent);
    175             ProvisionLogger.logi("Launched ACTION_STATE_USER_SETUP_COMPLETE with component "
    176                     + targetComponent);
    177         }
    178     }
    179 
    180     @Override
    181     public void progressUpdate(int progressMessage) {
    182     }
    183 
    184     @Override
    185     public void error(int titleId, int messageId, boolean resetRequired) {
    186         SimpleDialog.Builder dialogBuilder = new SimpleDialog.Builder()
    187                 .setTitle(titleId)
    188                 .setMessage(messageId)
    189                 .setCancelable(false)
    190                 .setPositiveButtonMessage(resetRequired
    191                         ? R.string.reset : R.string.device_owner_error_ok);
    192 
    193         showDialog(dialogBuilder, resetRequired ? ERROR_DIALOG_RESET : ERROR_DIALOG_OK);
    194     }
    195 
    196     @Override
    197     protected void showDialog(DialogBuilder builder, String tag) {
    198         // Whenever a dialog is shown, stop listening for further updates
    199         getProvisioningManager().unregisterListener(this);
    200         super.showDialog(builder, tag);
    201     }
    202 
    203     @Override
    204     protected int getMetricsCategory() {
    205         return PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS;
    206     }
    207 
    208     private void showCancelProvisioningDialog() {
    209         final boolean isDoProvisioning = getUtils().isDeviceOwnerAction(mParams.provisioningAction);
    210         final String dialogTag = isDoProvisioning ? CANCEL_PROVISIONING_DIALOG_RESET
    211                 : CANCEL_PROVISIONING_DIALOG_OK;
    212         final int positiveResId = isDoProvisioning ? R.string.reset
    213                 : R.string.profile_owner_cancel_ok;
    214         final int negativeResId = isDoProvisioning ? R.string.device_owner_cancel_cancel
    215                 : R.string.profile_owner_cancel_cancel;
    216         final int dialogMsgResId = isDoProvisioning
    217                 ? R.string.this_will_reset_take_back_first_screen
    218                 : R.string.profile_owner_cancel_message;
    219 
    220         SimpleDialog.Builder dialogBuilder = new SimpleDialog.Builder()
    221                 .setCancelable(false)
    222                 .setMessage(dialogMsgResId)
    223                 .setNegativeButtonMessage(negativeResId)
    224                 .setPositiveButtonMessage(positiveResId);
    225         if (isDoProvisioning) {
    226             dialogBuilder.setTitle(R.string.stop_setup_reset_device_question);
    227         }
    228 
    229         showDialog(dialogBuilder, dialogTag);
    230     }
    231 
    232     private void onProvisioningAborted() {
    233         setResult(Activity.RESULT_CANCELED);
    234         finish();
    235     }
    236 
    237     @Override
    238     public void onNegativeButtonClick(DialogFragment dialog) {
    239         switch (dialog.getTag()) {
    240             case CANCEL_PROVISIONING_DIALOG_OK:
    241             case CANCEL_PROVISIONING_DIALOG_RESET:
    242                 dialog.dismiss();
    243                 break;
    244             default:
    245                 SimpleDialog.throwButtonClickHandlerNotImplemented(dialog);
    246         }
    247         getProvisioningManager().registerListener(this);
    248     }
    249 
    250     @Override
    251     public void onPositiveButtonClick(DialogFragment dialog) {
    252         switch (dialog.getTag()) {
    253             case CANCEL_PROVISIONING_DIALOG_OK:
    254                 getProvisioningManager().cancelProvisioning();
    255                 onProvisioningAborted();
    256                 break;
    257             case CANCEL_PROVISIONING_DIALOG_RESET:
    258                 getUtils().sendFactoryResetBroadcast(this, "DO provisioning cancelled by user");
    259                 onProvisioningAborted();
    260                 break;
    261             case ERROR_DIALOG_OK:
    262                 onProvisioningAborted();
    263                 break;
    264             case ERROR_DIALOG_RESET:
    265                 getUtils().sendFactoryResetBroadcast(this, "Error during DO provisioning");
    266                 onProvisioningAborted();
    267                 break;
    268             default:
    269                 SimpleDialog.throwButtonClickHandlerNotImplemented(dialog);
    270         }
    271     }
    272 
    273     private void initializeUi(ProvisioningParams params) {
    274         final boolean isDoProvisioning = getUtils().isDeviceOwnerAction(params.provisioningAction);
    275         final int headerResId = isDoProvisioning ? R.string.setup_work_device
    276                 : R.string.setting_up_workspace;
    277         final int titleResId = isDoProvisioning ? R.string.setup_device_progress
    278                 : R.string.setup_profile_progress;
    279 
    280         initializeLayoutParams(R.layout.progress, headerResId, true,
    281                 CustomizationParams.createInstance(mParams, this, mUtils).statusBarColor);
    282         setTitle(titleResId);
    283     }
    284 }