• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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