• 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 android.app.admin.DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE;
20 
21 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS;
22 import static com.android.internal.util.Preconditions.checkNotNull;
23 
24 import static java.util.Objects.requireNonNull;
25 
26 import android.Manifest.permission;
27 import android.annotation.IntDef;
28 import android.app.Activity;
29 import android.app.admin.DevicePolicyManager;
30 import android.content.ComponentName;
31 import android.content.Intent;
32 import android.content.pm.PackageManager;
33 import android.content.pm.ResolveInfo;
34 import android.os.Bundle;
35 import android.os.UserHandle;
36 import android.view.ViewGroup;
37 
38 import androidx.annotation.VisibleForTesting;
39 
40 import com.android.managedprovisioning.ManagedProvisioningScreens;
41 import com.android.managedprovisioning.R;
42 import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences;
43 import com.android.managedprovisioning.common.PolicyComplianceUtils;
44 import com.android.managedprovisioning.common.ProvisionLogger;
45 import com.android.managedprovisioning.common.SettingsFacade;
46 import com.android.managedprovisioning.common.ThemeHelper;
47 import com.android.managedprovisioning.common.ThemeHelper.DefaultNightModeChecker;
48 import com.android.managedprovisioning.common.ThemeHelper.DefaultSetupWizardBridge;
49 import com.android.managedprovisioning.common.Utils;
50 import com.android.managedprovisioning.finalization.PreFinalizationController;
51 import com.android.managedprovisioning.finalization.UserProvisioningStateHelper;
52 import com.android.managedprovisioning.model.ProvisioningParams;
53 import com.android.managedprovisioning.provisioning.TransitionAnimationHelper.TransitionAnimationCallback;
54 import com.android.managedprovisioning.provisioning.TransitionAnimationHelper.TransitionAnimationStateManager;
55 
56 import com.airbnb.lottie.LottieAnimationView;
57 import com.google.android.setupcompat.util.WizardManagerHelper;
58 import com.google.android.setupdesign.util.Partner;
59 
60 import java.lang.annotation.Retention;
61 import java.lang.annotation.RetentionPolicy;
62 import java.util.Collections;
63 import java.util.HashMap;
64 import java.util.List;
65 import java.util.Map;
66 
67 /**
68  * Progress activity shown whilst provisioning is ongoing.
69  *
70  * <p>This activity registers for updates of the provisioning process from the
71  * {@link ProvisioningManager}. It shows progress updates as provisioning progresses and handles
72  * showing of cancel and error dialogs.</p>
73  */
74 public class ProvisioningActivity extends AbstractProvisioningActivity
75         implements TransitionAnimationCallback, TransitionAnimationStateManager {
76     private static final int RESULT_CODE_COMPLETE_DEVICE_FINANCE = 121;
77     /*
78      * Returned after the work profile has been completed. Note this is before launching the DPC.
79      */
80     @VisibleForTesting
81     static final int RESULT_CODE_WORK_PROFILE_CREATED = 122;
82     /*
83      * Returned after the device owner has been set. Note this is before launching the DPC.
84      */
85     @VisibleForTesting
86     static final int RESULT_CODE_DEVICE_OWNER_SET = 123;
87 
88     static final int PROVISIONING_MODE_WORK_PROFILE = 1;
89     static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 2;
90     static final int PROVISIONING_MODE_WORK_PROFILE_ON_FULLY_MANAGED_DEVICE = 3;
91     static final int PROVISIONING_MODE_FINANCED_DEVICE = 4;
92     static final int PROVISIONING_MODE_WORK_PROFILE_ON_ORG_OWNED_DEVICE = 5;
93     private ViewGroup mButtonFooterContainer;
94 
95     @IntDef(prefix = { "PROVISIONING_MODE_" }, value = {
96         PROVISIONING_MODE_WORK_PROFILE,
97         PROVISIONING_MODE_FULLY_MANAGED_DEVICE,
98         PROVISIONING_MODE_WORK_PROFILE_ON_FULLY_MANAGED_DEVICE,
99         PROVISIONING_MODE_FINANCED_DEVICE,
100         PROVISIONING_MODE_WORK_PROFILE_ON_ORG_OWNED_DEVICE
101     })
102     @Retention(RetentionPolicy.SOURCE)
103     @interface ProvisioningMode {}
104 
105     private static final Map<Integer, Integer> PROVISIONING_MODE_TO_PROGRESS_LABEL =
106             Collections.unmodifiableMap(new HashMap<Integer, Integer>() {{
107                 put(PROVISIONING_MODE_WORK_PROFILE,
108                         R.string.work_profile_provisioning_progress_label);
109                 put(PROVISIONING_MODE_FULLY_MANAGED_DEVICE,
110                         R.string.fully_managed_device_provisioning_progress_label);
111                 put(PROVISIONING_MODE_WORK_PROFILE_ON_FULLY_MANAGED_DEVICE,
112                         R.string.fully_managed_device_provisioning_progress_label);
113                 put(PROVISIONING_MODE_FINANCED_DEVICE, R.string.just_a_sec);
114                 put(PROVISIONING_MODE_WORK_PROFILE_ON_ORG_OWNED_DEVICE,
115                         R.string.work_profile_provisioning_progress_label);
116             }});
117 
118     private UserProvisioningStateHelper mUserProvisioningStateHelper;
119     private PolicyComplianceUtils mPolicyComplianceUtils;
120     private ProvisioningManager mProvisioningManager;
121     private ProvisioningActivityBridge mBridge;
122 
ProvisioningActivity()123     public ProvisioningActivity() {
124         this(
125                 /* provisioningManager */ null, // defined in getProvisioningManager()
126                 new Utils(),
127                 /* userProvisioningStateHelper */ null, // defined in onCreate()
128                 new PolicyComplianceUtils(),
129                 new SettingsFacade(),
130                 new ThemeHelper(new DefaultNightModeChecker(), new DefaultSetupWizardBridge()));
131     }
132 
133     @VisibleForTesting
ProvisioningActivity(ProvisioningManager provisioningManager, Utils utils, UserProvisioningStateHelper userProvisioningStateHelper, PolicyComplianceUtils policyComplianceUtils, SettingsFacade settingsFacade, ThemeHelper themeHelper)134     public ProvisioningActivity(ProvisioningManager provisioningManager,
135             Utils utils,
136             UserProvisioningStateHelper userProvisioningStateHelper,
137             PolicyComplianceUtils policyComplianceUtils,
138             SettingsFacade settingsFacade,
139             ThemeHelper themeHelper) {
140         super(utils, settingsFacade, themeHelper);
141         mProvisioningManager = provisioningManager;
142         mUserProvisioningStateHelper = userProvisioningStateHelper;
143         mPolicyComplianceUtils = checkNotNull(policyComplianceUtils);
144     }
145 
146     @Override
onCreate(Bundle savedInstanceState)147     protected void onCreate(Bundle savedInstanceState) {
148         super.onCreate(savedInstanceState);
149         mBridge = createBridge();
150         mBridge.initiateUi(/* activity= */ this);
151 
152         // assign this Activity as the view store owner to access saved state and receive updates
153         getProvisioningManager().setViewModelStoreOwner(this);
154 
155         if (mUserProvisioningStateHelper == null) {
156             mUserProvisioningStateHelper = new UserProvisioningStateHelper(this);
157         }
158 
159         if (mState == STATE_PROVISIONING_FINALIZED) {
160             updateProvisioningFinalizedScreen();
161         }
162 
163         writeSharedPreferences();
164     }
165 
writeSharedPreferences()166     private void writeSharedPreferences() {
167         ManagedProvisioningSharedPreferences sharedPreferences =
168                 new ManagedProvisioningSharedPreferences(this);
169         sharedPreferences.writeNavigationBarColor(getWindow().getNavigationBarColor());
170         sharedPreferences.writeNavigationBarDividerColor(
171                 getWindow().getNavigationBarDividerColor());
172         sharedPreferences.writeTextPrimaryColor(mUtils.getTextPrimaryColor(this));
173         sharedPreferences.writeTextSecondaryColor(mUtils.getTextSecondaryColor(this));
174         sharedPreferences.writeBackgroundColor(mUtils.getBackgroundColor(this));
175         sharedPreferences.writeAccentColor(mUtils.getAccentColor(this));
176         sharedPreferences.writeNotificationBackgroundColor(
177                 Partner.getColor(this, R.color.setup_notification_bg_color));
178     }
179 
createBridge()180     protected ProvisioningActivityBridge createBridge() {
181         return ProvisioningActivityBridgeImpl.builder()
182                 .setParams(mParams)
183                 .setUtils(mUtils)
184                 .setProvisioningMode(getProvisioningMode())
185                 .setProvisioningManager(getProvisioningManager())
186                 .setTransitionAnimationCallback(this)
187                 .setInitializeLayoutParamsConsumer(
188                         ProvisioningActivity.this::initializeLayoutParams)
189                 .setShouldSkipEducationScreens(shouldSkipEducationScreens())
190                 .setProgressLabelResId(getProgressLabelResId())
191                 .setBridgeCallbacks(createCallbacks())
192                 .setStateManager(this)
193                 .build();
194     }
195 
getProgressLabelResId()196     protected Integer getProgressLabelResId() {
197         return PROVISIONING_MODE_TO_PROGRESS_LABEL.get(getProvisioningMode());
198     }
199 
createCallbacks()200     protected final ProvisioningActivityBridgeCallbacks createCallbacks() {
201         return new ProvisioningActivityBridgeCallbacks() {
202             @Override
203             public void onNextButtonClicked() {
204                 ProvisioningActivity.this.onNextButtonClicked();
205             }
206 
207             @Override
208             public void onAbortButtonClicked() {
209                 ProvisioningActivity.this.onAbortButtonClicked();
210             }
211 
212             @Override
213             public boolean isProvisioningFinalized() {
214                 return mState == STATE_PROVISIONING_FINALIZED;
215             }
216         };
217     }
218 
219     @Override
220     protected ProvisioningManager getProvisioningManager() {
221         if (mProvisioningManager == null) {
222             mProvisioningManager = ProvisioningManager.getInstance(this);
223         }
224         return mProvisioningManager;
225     }
226 
227     @VisibleForTesting
228     protected void setProvisioningManager(ProvisioningManager provisioningManager) {
229         mProvisioningManager = requireNonNull(provisioningManager);
230     }
231 
232     @Override
233     public void preFinalizationCompleted() {
234         if (mState == STATE_PROVISIONING_COMPLETED || mState == STATE_PROVISIONING_FINALIZED) {
235             return;
236         }
237 
238         if (!validatePolicyComplianceExists()) {
239             ProvisionLogger.loge("POLICY_COMPLIANCE handler not implemented by the admin app.");
240             error(R.string.cant_set_up_device,
241                     R.string.contact_your_admin_for_help,
242                     /* resetRequired */ mParams.isOrganizationOwnedProvisioning);
243             return;
244         }
245 
246         ProvisionLogger.logi("ProvisioningActivity pre-finalization completed");
247 
248         // TODO(183094412): Decouple state from AbstractProvisioningActivity
249         mState = STATE_PROVISIONING_COMPLETED;
250 
251         if (shouldSkipEducationScreens()
252                 || mBridge.shouldShowButtonsWhenPreProvisioningCompletes()) {
253             updateProvisioningFinalizedScreen();
254         }
255     }
256 
257     // Enforces DPCs to implement the POLICY_COMPLIANCE handler for NFC and financed device
258     // provisioning, since we no longer set up the DPC on setup wizard's exit procedure.
259     // No need to verify it for the other flows, as that was already done earlier.
260     // TODO(b/177849035): Remove NFC and financed device-specific logic
261     private boolean validatePolicyComplianceExists() {
262         if (!mParams.isNfc && !mUtils.isFinancedDeviceAction(mParams.provisioningAction)) {
263             return true;
264         }
265         return mPolicyComplianceUtils.isPolicyComplianceActivityResolvableForUser(
266                 this, mParams, mUtils, UserHandle.SYSTEM);
267     }
268 
269     protected final void updateProvisioningFinalizedScreen() {
270         mBridge.onProvisioningFinalized(/* activity= */ this);
271 
272         // TODO(183094412): Decouple state from AbstractProvisioningActivity
273         mState = STATE_PROVISIONING_FINALIZED;
274     }
275 
276     @VisibleForTesting
277     protected void onNextButtonClicked() {
278         markDeviceManagementEstablishedAndFinish();
279     }
280 
281     @VisibleForTesting
282     protected void onAbortButtonClicked() {
283         final Intent intent = new Intent(this,
284                 getActivityForScreen(ManagedProvisioningScreens.RESET_AND_RETURN_DEVICE));
285         WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent);
286         intent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, mParams);
287         getTransitionHelper().startActivityWithTransition(this, intent);
288     }
289 
290     private void finishActivity() {
291         if (mParams.provisioningAction.equals(ACTION_PROVISION_FINANCED_DEVICE)) {
292             setResult(RESULT_CODE_COMPLETE_DEVICE_FINANCE);
293         } else {
294             setResult(Activity.RESULT_OK);
295         }
296         maybeLaunchNfcUserSetupCompleteIntent();
297         getTransitionHelper().finishActivity(this);
298     }
299 
300     private void markDeviceManagementEstablishedAndFinish() {
301         new PreFinalizationController(this, mUserProvisioningStateHelper)
302                 .deviceManagementEstablished(mParams);
303         if (mParams.flowType == ProvisioningParams.FLOW_TYPE_ADMIN_INTEGRATED) {
304             if (mUtils.isProfileOwnerAction(mParams.provisioningAction)) {
305                 setResult(RESULT_CODE_WORK_PROFILE_CREATED);
306             } else if (mUtils.isDeviceOwnerAction(mParams.provisioningAction)) {
307                 setResult(RESULT_CODE_DEVICE_OWNER_SET);
308             } else if (mUtils.isFinancedDeviceAction(mParams.provisioningAction)) {
309                 setResult(RESULT_CODE_COMPLETE_DEVICE_FINANCE);
310             }
311             getTransitionHelper().finishActivity(this);
312         } else {
313             finishActivity();
314         }
315     }
316 
317     private void maybeLaunchNfcUserSetupCompleteIntent() {
318         if (mParams != null && mParams.isNfc) {
319             // Start SetupWizard to complete the intent.
320             final Intent intent = new Intent(DevicePolicyManager.ACTION_STATE_USER_SETUP_COMPLETE)
321                     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
322             final PackageManager pm = getPackageManager();
323             List<ResolveInfo> ris = pm.queryIntentActivities(intent, 0);
324 
325             // Look for the first legitimate component protected by the permission
326             ComponentName targetComponent = null;
327             for (ResolveInfo ri : ris) {
328                 if (ri.activityInfo == null) {
329                     continue;
330                 }
331                 if (!permission.BIND_DEVICE_ADMIN.equals(ri.activityInfo.permission)) {
332                     ProvisionLogger.loge("Component " + ri.activityInfo.getComponentName()
333                             + " is not protected by " + permission.BIND_DEVICE_ADMIN);
334                 } else if (pm.checkPermission(permission.DISPATCH_PROVISIONING_MESSAGE,
335                         ri.activityInfo.packageName) != PackageManager.PERMISSION_GRANTED) {
336                     ProvisionLogger.loge("Package " + ri.activityInfo.packageName
337                             + " does not have " + permission.DISPATCH_PROVISIONING_MESSAGE);
338                 } else {
339                     targetComponent = ri.activityInfo.getComponentName();
340                     break;
341                 }
342             }
343 
344             if (targetComponent == null) {
345                 ProvisionLogger.logw("No activity accepts intent ACTION_STATE_USER_SETUP_COMPLETE");
346                 return;
347             }
348 
349             intent.setComponent(targetComponent);
350             getTransitionHelper().startActivityWithTransition(this, intent);
351             ProvisionLogger.logi("Launched ACTION_STATE_USER_SETUP_COMPLETE with component "
352                     + targetComponent);
353         }
354     }
355 
356     @Override
357     protected int getMetricsCategory() {
358         return PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS;
359     }
360 
361     @Override
362     protected void decideCancelProvisioningDialog() {
363         if ((mState == STATE_PROVISIONING_COMPLETED || mState == STATE_PROVISIONING_FINALIZED)
364                 && !mParams.isOrganizationOwnedProvisioning) {
365             return;
366         }
367 
368         if (getUtils().isDeviceOwnerAction(mParams.provisioningAction)
369                 || mParams.isOrganizationOwnedProvisioning) {
370             showCancelProvisioningDialog(/* resetRequired = */true);
371         } else {
372             showCancelProvisioningDialog(/* resetRequired = */false);
373         }
374     }
375 
376     @Override
377     protected void onStart() {
378         super.onStart();
379         mBridge.onStart(this);
380     }
381 
382     @Override
383     protected void onStop() {
384         super.onStop();
385         mBridge.onStop();
386         // remove this Activity as the view store owner to avoid memory leaks
387         if (isFinishing()) {
388             getProvisioningManager().clearViewModelStoreOwner();
389         }
390     }
391 
392     @Override
393     public void onAllTransitionsShown() {
394         if (mState == STATE_PROVISIONING_COMPLETED) {
395             updateProvisioningFinalizedScreen();
396         }
397     }
398 
399     @Override
400     public void onAnimationSetup(LottieAnimationView animationView) {
401         getThemeHelper().setupAnimationDynamicColors(this, animationView, getIntent());
402     }
403 
404     @Override
405     public void saveState(TransitionAnimationHelper.TransitionAnimationState state) {
406         getProvisioningManager().saveTransitionAnimationState(state);
407     }
408 
409     @Override
410     public TransitionAnimationHelper.TransitionAnimationState restoreState() {
411         return getProvisioningManager().restoreTransitionAnimationState();
412     }
413 
414     @Override
415     protected boolean isWaitingScreen() {
416         return shouldSkipEducationScreens();
417     }
418 
419     protected @ProvisioningMode int getProvisioningMode() {
420         int provisioningMode = 0;
421         final boolean isProfileOwnerAction =
422                 mUtils.isProfileOwnerAction(mParams.provisioningAction);
423         if (isProfileOwnerAction) {
424             if (getSystemService(DevicePolicyManager.class).isDeviceManaged()) {
425                 provisioningMode = PROVISIONING_MODE_WORK_PROFILE_ON_FULLY_MANAGED_DEVICE;
426             } else if (mParams.isOrganizationOwnedProvisioning) {
427                 provisioningMode = PROVISIONING_MODE_WORK_PROFILE_ON_ORG_OWNED_DEVICE;
428             } else {
429                 provisioningMode = PROVISIONING_MODE_WORK_PROFILE;
430             }
431         } else if (mUtils.isDeviceOwnerAction(mParams.provisioningAction)) {
432             provisioningMode = PROVISIONING_MODE_FULLY_MANAGED_DEVICE;
433         } else if (mUtils.isFinancedDeviceAction(mParams.provisioningAction)) {
434             provisioningMode = PROVISIONING_MODE_FINANCED_DEVICE;
435         }
436         return provisioningMode;
437     }
438 
439     protected boolean shouldSkipEducationScreens() {
440         return mParams.skipEducationScreens
441                 || getProvisioningMode() == PROVISIONING_MODE_WORK_PROFILE_ON_FULLY_MANAGED_DEVICE
442                 || getProvisioningMode() == PROVISIONING_MODE_FINANCED_DEVICE;
443     }
444 }
445