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