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