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