1 /* 2 * Copyright (C) 2021 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.preprovisioning; 18 19 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS; 20 21 import static java.util.Objects.requireNonNull; 22 23 import android.annotation.IntDef; 24 import android.annotation.MainThread; 25 import android.annotation.Nullable; 26 import android.content.Intent; 27 import android.os.PersistableBundle; 28 29 import androidx.lifecycle.LiveData; 30 import androidx.lifecycle.MutableLiveData; 31 import androidx.lifecycle.ViewModel; 32 import androidx.lifecycle.ViewModelProvider; 33 34 import com.android.managedprovisioning.ManagedProvisioningBaseApplication; 35 import com.android.managedprovisioning.analytics.TimeLogger; 36 import com.android.managedprovisioning.common.IllegalProvisioningArgumentException; 37 import com.android.managedprovisioning.common.ProvisionLogger; 38 import com.android.managedprovisioning.common.Utils; 39 import com.android.managedprovisioning.model.ProvisioningParams; 40 import com.android.managedprovisioning.parser.MessageParser; 41 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 45 /** 46 * A {@link ViewModel} which maintains data related to preprovisioning. 47 */ 48 public final class PreProvisioningViewModel extends ViewModel { 49 static final int STATE_PREPROVISIONING_INITIALIZING = 1; 50 static final int STATE_PREPROVISIONING_INITIALIZED = 2; 51 static final int STATE_GETTING_PROVISIONING_MODE = 3; 52 static final int STATE_SHOWING_USER_CONSENT = 4; 53 static final int STATE_PROVISIONING_STARTED = 5; 54 static final int STATE_PROVISIONING_FINALIZED = 6; 55 static final int STATE_UPDATING_ROLE_HOLDER = 7; 56 static final int STATE_ROLE_HOLDER_PROVISIONING = 8; 57 58 private static final int DEFAULT_MAX_ROLE_HOLDER_UPDATE_RETRIES = 3; 59 private PersistableBundle mRoleHolderState; 60 61 @Retention(RetentionPolicy.SOURCE) 62 @IntDef({STATE_PREPROVISIONING_INITIALIZING, 63 STATE_GETTING_PROVISIONING_MODE, 64 STATE_SHOWING_USER_CONSENT, 65 STATE_PROVISIONING_STARTED, 66 STATE_PROVISIONING_FINALIZED, 67 STATE_UPDATING_ROLE_HOLDER, 68 STATE_ROLE_HOLDER_PROVISIONING}) 69 private @interface PreProvisioningState {} 70 71 72 private ProvisioningParams mParams; 73 private final MessageParser mMessageParser; 74 private final TimeLogger mTimeLogger; 75 private final EncryptionController mEncryptionController; 76 private final MutableLiveData<Integer> mState = 77 new MutableLiveData<>(STATE_PREPROVISIONING_INITIALIZING); 78 private final Config mConfig; 79 private int mRoleHolderUpdateRetries = 1; 80 PreProvisioningViewModel( TimeLogger timeLogger, MessageParser messageParser, EncryptionController encryptionController, Config config)81 PreProvisioningViewModel( 82 TimeLogger timeLogger, 83 MessageParser messageParser, 84 EncryptionController encryptionController, 85 Config config) { 86 mMessageParser = requireNonNull(messageParser); 87 mTimeLogger = requireNonNull(timeLogger); 88 mEncryptionController = requireNonNull(encryptionController); 89 mConfig = requireNonNull(config); 90 } 91 92 /** 93 * Updates state after provisioning has completed 94 */ 95 @MainThread onReturnFromProvisioning()96 public void onReturnFromProvisioning() { 97 setState(STATE_PROVISIONING_FINALIZED); 98 } 99 100 /** 101 * Handles state when the admin-integrated flow was initiated 102 */ 103 @MainThread onAdminIntegratedFlowInitiated()104 public void onAdminIntegratedFlowInitiated() { 105 setState(STATE_GETTING_PROVISIONING_MODE); 106 } 107 108 /** 109 * Handles state when user consent is shown 110 */ 111 @MainThread onShowUserConsent()112 public void onShowUserConsent() { 113 setState(STATE_SHOWING_USER_CONSENT); 114 } 115 116 /** 117 * Handles state when provisioning is started after the user consent 118 */ 119 @MainThread onProvisioningStartedAfterUserConsent()120 public void onProvisioningStartedAfterUserConsent() { 121 setState(STATE_PROVISIONING_STARTED); 122 } 123 124 /** 125 * Handles state when provisioning has initiated 126 */ 127 @MainThread onPlatformProvisioningInitiated()128 public void onPlatformProvisioningInitiated() { 129 setState(STATE_PREPROVISIONING_INITIALIZED); 130 } 131 132 /** 133 * Handles state when the role holder update has started 134 */ 135 @MainThread onRoleHolderUpdateInitiated()136 public void onRoleHolderUpdateInitiated() { 137 setState(STATE_UPDATING_ROLE_HOLDER); 138 } 139 140 /** 141 * Handles state when provisioning is delegated to the role holder 142 */ 143 @MainThread onRoleHolderProvisioningInitiated()144 public void onRoleHolderProvisioningInitiated() { 145 setState(STATE_ROLE_HOLDER_PROVISIONING); 146 } 147 148 /** 149 * Sets a copy of the role holder state. 150 * @param roleHolderState 151 */ setRoleHolderState(@ullable PersistableBundle roleHolderState)152 public void setRoleHolderState(@Nullable PersistableBundle roleHolderState) { 153 if (roleHolderState == null) { 154 mRoleHolderState = null; 155 } else { 156 mRoleHolderState = new PersistableBundle(roleHolderState); 157 } 158 } 159 160 /** 161 * Retrieves a copy of the role holder state or {@link null} if one does not exist. 162 * @return 163 */ getRoleHolderState()164 public @Nullable PersistableBundle getRoleHolderState() { 165 if (mRoleHolderState == null) { 166 return null; 167 } else { 168 return new PersistableBundle(mRoleHolderState); 169 } 170 } 171 172 /** 173 * Returns the state as a {@link LiveData}. 174 */ getState()175 LiveData<Integer> getState() { 176 return mState; 177 } 178 179 /** 180 * Loads the {@link ProvisioningParams} if they haven't been loaded before. 181 * 182 * @throws IllegalProvisioningArgumentException if the intent extras are invalid 183 */ loadParamsIfNecessary(Intent intent)184 void loadParamsIfNecessary(Intent intent) throws IllegalProvisioningArgumentException { 185 if (mParams == null) { 186 mParams = loadProvisioningParams(intent); 187 } 188 } 189 190 /** 191 * Returns the {@link ProvisioningParams} associated with this provisioning session. 192 */ getParams()193 ProvisioningParams getParams() { 194 if (mParams == null) { 195 throw new IllegalStateException("Trying to get params without loading them first. " 196 + "Did you run loadParamsIfNecessary(Intent)?"); 197 } 198 return mParams; 199 } 200 201 /** 202 * Replaces the cached {@link ProvisioningParams} with {@code params}. 203 */ updateParams(ProvisioningParams params)204 void updateParams(ProvisioningParams params) { 205 if (params == null) { 206 throw new IllegalArgumentException("Cannot update params to null."); 207 } 208 mParams = params; 209 } 210 211 /** 212 * Returns the cached {@link TimeLogger} instance. 213 */ getTimeLogger()214 TimeLogger getTimeLogger() { 215 return mTimeLogger; 216 } 217 218 /** 219 * Returns the cached {@link EncryptionController} instance. 220 */ getEncryptionController()221 EncryptionController getEncryptionController() { 222 return mEncryptionController; 223 } 224 loadProvisioningParams(Intent intent)225 private ProvisioningParams loadProvisioningParams(Intent intent) 226 throws IllegalProvisioningArgumentException { 227 return mMessageParser.parse(intent); 228 } 229 230 /** 231 * Sets the state which updates all the listening obervables. 232 * <p>This method must be called from the UI thread. 233 */ 234 @MainThread setState(@reProvisioningState int state)235 private void setState(@PreProvisioningState int state) { 236 if (mState.getValue() != state) { 237 ProvisionLogger.logi( 238 "Setting preprovisioning state to " + state + ", previous state was " 239 + mState.getValue()); 240 mState.setValue(state); 241 } else { 242 ProvisionLogger.logi( 243 "Attempt to set preprovisioning state to the same state " + mState); 244 } 245 } 246 incrementRoleHolderUpdateRetryCount()247 void incrementRoleHolderUpdateRetryCount() { 248 mRoleHolderUpdateRetries++; 249 } 250 251 /** 252 * Resets the update retry count 253 */ resetRoleHolderUpdateRetryCount()254 public void resetRoleHolderUpdateRetryCount() { 255 mRoleHolderUpdateRetries = 0; 256 } 257 canRetryRoleHolderUpdate()258 boolean canRetryRoleHolderUpdate() { 259 return mRoleHolderUpdateRetries < mConfig.getMaxRoleHolderUpdateRetries(); 260 } 261 262 interface Config { getMaxRoleHolderUpdateRetries()263 int getMaxRoleHolderUpdateRetries(); 264 } 265 266 static class DefaultConfig implements Config { 267 @Override getMaxRoleHolderUpdateRetries()268 public int getMaxRoleHolderUpdateRetries() { 269 return DEFAULT_MAX_ROLE_HOLDER_UPDATE_RETRIES; 270 } 271 } 272 273 static class PreProvisioningViewModelFactory implements ViewModelProvider.Factory { 274 private final ManagedProvisioningBaseApplication mApplication; 275 private final Config mConfig; 276 private final Utils mUtils; 277 PreProvisioningViewModelFactory( ManagedProvisioningBaseApplication application, Config config, Utils utils)278 PreProvisioningViewModelFactory( 279 ManagedProvisioningBaseApplication application, 280 Config config, 281 Utils utils) { 282 mApplication = requireNonNull(application); 283 mConfig = requireNonNull(config); 284 mUtils = requireNonNull(utils); 285 } 286 287 @Override 288 @SuppressWarnings("unchecked") create(Class<T> modelClass)289 public <T extends ViewModel> T create(Class<T> modelClass) { 290 if (!PreProvisioningViewModel.class.isAssignableFrom(modelClass)) { 291 throw new IllegalArgumentException("Invalid class for creating a " 292 + "PreProvisioningViewModel: " + modelClass); 293 } 294 return (T) new PreProvisioningViewModel( 295 new TimeLogger(mApplication, PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS), 296 new MessageParser(mApplication, mUtils), 297 mApplication.getEncryptionController(), 298 mConfig); 299 } 300 } 301 } 302