1 /* 2 * Copyright (C) 2023 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.devicelockcontroller.storage; 18 19 import android.annotation.CurrentTimeMillisLong; 20 import android.content.Context; 21 import android.content.SharedPreferences; 22 import android.os.Build; 23 24 import androidx.annotation.Nullable; 25 import androidx.annotation.WorkerThread; 26 27 import com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionState; 28 import com.android.devicelockcontroller.util.LogUtil; 29 import com.android.devicelockcontroller.util.ThreadAsserts; 30 31 import java.util.Locale; 32 import java.util.concurrent.Executors; 33 34 /** 35 * Stores per-user local parameters. 36 * Unlike {@link GlobalParameters}, this class can be directly accessed. 37 */ 38 public final class UserParameters { 39 private static final String FILENAME = "user-params"; 40 private static final String TAG = "UserParameters"; 41 private static final String KEY_PROVISION_STATE = "provision-state"; 42 private static final String KEY_BOOT_TIME_MILLS = "boot-time-mills"; 43 private static final String KEY_NEXT_CHECK_IN_TIME_MILLIS = "next-check-in-time-millis"; 44 private static final String KEY_RESUME_PROVISION_TIME_MILLIS = 45 "resume-provision-time-millis"; 46 private static final String KEY_NEXT_PROVISION_FAILED_STEP_TIME_MILLIS = 47 "next-provision-failed-step-time-millis"; 48 private static final String KEY_RESET_DEVICE_TIME_MILLIS = "reset-device-time-millis"; 49 private static final String KEY_DAYS_LEFT_UNTIL_RESET = "days-left-until-reset"; 50 private static final String KEY_PROVISIONING_START_TIME_MILLIS = 51 "provisioning-start-time-millis"; 52 public static final String KEY_NEED_INITIAL_CHECK_IN = "need-initial-check-in"; 53 public static final String KEY_NOTIFICATION_CHANNEL_ID_SUFFIX = 54 "notification-channel-id-suffix"; 55 public static final String KEY_SUW_TIMED_OUT = "suw-timed-out"; 56 UserParameters()57 private UserParameters() { 58 } 59 getSharedPreferences(Context context)60 private static SharedPreferences getSharedPreferences(Context context) { 61 final Context deviceContext = context.createDeviceProtectedStorageContext(); 62 63 return deviceContext.getSharedPreferences(FILENAME, Context.MODE_PRIVATE); 64 } 65 66 /** 67 * Gets the current user state. 68 */ 69 @WorkerThread 70 @ProvisionState getProvisionState(Context context)71 public static int getProvisionState(Context context) { 72 ThreadAsserts.assertWorkerThread("getProvisionState"); 73 return getSharedPreferences(context).getInt(KEY_PROVISION_STATE, 74 ProvisionState.UNPROVISIONED); 75 } 76 77 /** 78 * Sets the current user state. 79 */ setProvisionState(Context context, @ProvisionState int state)80 public static void setProvisionState(Context context, @ProvisionState int state) { 81 getSharedPreferences(context).edit().putInt(KEY_PROVISION_STATE, state).apply(); 82 } 83 84 /** Check if initial check-in is required. */ 85 @WorkerThread needInitialCheckIn(Context context)86 public static boolean needInitialCheckIn(Context context) { 87 ThreadAsserts.assertWorkerThread("needInitialCheckIn"); 88 return getSharedPreferences(context).getBoolean(KEY_NEED_INITIAL_CHECK_IN, true); 89 } 90 91 /** Mark initial check-in has been scheduled. */ initialCheckInScheduled(Context context)92 public static void initialCheckInScheduled(Context context) { 93 getSharedPreferences(context).edit().putBoolean(KEY_NEED_INITIAL_CHECK_IN, false).apply(); 94 } 95 96 /** Get the device boot time */ 97 @WorkerThread 98 @CurrentTimeMillisLong getBootTimeMillis(Context context)99 public static long getBootTimeMillis(Context context) { 100 ThreadAsserts.assertWorkerThread("getBootTimeMillis"); 101 return getSharedPreferences(context).getLong(KEY_BOOT_TIME_MILLS, 0L); 102 } 103 104 /** Set the time when device boot */ setBootTimeMillis(Context context, @CurrentTimeMillisLong long bootTime)105 public static void setBootTimeMillis(Context context, @CurrentTimeMillisLong long bootTime) { 106 getSharedPreferences(context).edit().putLong(KEY_BOOT_TIME_MILLS, bootTime).apply(); 107 } 108 109 /** Get the time when next check in should happen */ 110 @WorkerThread 111 @CurrentTimeMillisLong getNextCheckInTimeMillis(Context context)112 public static long getNextCheckInTimeMillis(Context context) { 113 ThreadAsserts.assertWorkerThread("getNextCheckInTimeMillis"); 114 return getSharedPreferences(context).getLong(KEY_NEXT_CHECK_IN_TIME_MILLIS, 0L); 115 } 116 117 /** Set the time when next check in should happen */ setNextCheckInTimeMillis(Context context, @CurrentTimeMillisLong long nextCheckInTime)118 public static void setNextCheckInTimeMillis(Context context, 119 @CurrentTimeMillisLong long nextCheckInTime) { 120 getSharedPreferences(context).edit().putLong(KEY_NEXT_CHECK_IN_TIME_MILLIS, 121 nextCheckInTime).apply(); 122 } 123 124 /** Get the time when provision should resume */ 125 @WorkerThread 126 @CurrentTimeMillisLong getResumeProvisionTimeMillis(Context context)127 public static long getResumeProvisionTimeMillis(Context context) { 128 ThreadAsserts.assertWorkerThread("getResumeProvisionTimeMillis"); 129 return getSharedPreferences(context).getLong(KEY_RESUME_PROVISION_TIME_MILLIS, 0L); 130 } 131 132 /** Set the time when provision should resume */ setResumeProvisionTimeMillis(Context context, @CurrentTimeMillisLong long resumeProvisionTime)133 public static void setResumeProvisionTimeMillis(Context context, 134 @CurrentTimeMillisLong long resumeProvisionTime) { 135 getSharedPreferences(context).edit().putLong(KEY_RESUME_PROVISION_TIME_MILLIS, 136 resumeProvisionTime).apply(); 137 } 138 139 /** Get the time when next provision failed step should happen */ 140 @WorkerThread 141 @CurrentTimeMillisLong getNextProvisionFailedStepTimeMills(Context context)142 public static long getNextProvisionFailedStepTimeMills(Context context) { 143 ThreadAsserts.assertWorkerThread("getNextProvisionFailedStepTimeMills"); 144 return getSharedPreferences(context).getLong(KEY_NEXT_PROVISION_FAILED_STEP_TIME_MILLIS, 145 0L); 146 } 147 148 /** Set the time when next provision failed step should happen */ setNextProvisionFailedStepTimeMills(Context context, @CurrentTimeMillisLong long nextProvisionFailedStep)149 public static void setNextProvisionFailedStepTimeMills(Context context, 150 @CurrentTimeMillisLong long nextProvisionFailedStep) { 151 getSharedPreferences(context).edit().putLong(KEY_NEXT_PROVISION_FAILED_STEP_TIME_MILLIS, 152 nextProvisionFailedStep).apply(); 153 } 154 155 /** Get the time when device should factory reset */ 156 @WorkerThread 157 @CurrentTimeMillisLong getResetDeviceTimeMillis(Context context)158 public static long getResetDeviceTimeMillis(Context context) { 159 ThreadAsserts.assertWorkerThread("getResetDeviceTimeMillis"); 160 return getSharedPreferences(context).getLong(KEY_RESET_DEVICE_TIME_MILLIS, 0L); 161 } 162 163 /** Set the time when device should factory reset */ setResetDeviceTimeMillis(Context context, @CurrentTimeMillisLong long resetDeviceTime)164 public static void setResetDeviceTimeMillis(Context context, 165 @CurrentTimeMillisLong long resetDeviceTime) { 166 getSharedPreferences(context).edit().putLong(KEY_RESET_DEVICE_TIME_MILLIS, 167 resetDeviceTime).apply(); 168 } 169 170 /** Get the number of days before device should factory reset */ 171 @WorkerThread getDaysLeftUntilReset(Context context)172 public static int getDaysLeftUntilReset(Context context) { 173 ThreadAsserts.assertWorkerThread("getDaysLeftUntilReset"); 174 return getSharedPreferences(context).getInt(KEY_DAYS_LEFT_UNTIL_RESET, Integer.MAX_VALUE); 175 } 176 177 /** Set the number of days before device should factory reset */ setDaysLeftUntilReset(Context context, int days)178 public static void setDaysLeftUntilReset(Context context, int days) { 179 getSharedPreferences(context).edit().putInt(KEY_DAYS_LEFT_UNTIL_RESET, days).apply(); 180 } 181 182 /** Get the provisioning start time */ getProvisioningStartTimeMillis(Context context)183 public static long getProvisioningStartTimeMillis(Context context) { 184 return getSharedPreferences(context).getLong(KEY_PROVISIONING_START_TIME_MILLIS, 185 /* defValue = */-1L); 186 } 187 188 /** Set the provisioning start time */ setProvisioningStartTimeMillis(Context context, @CurrentTimeMillisLong long provisioningStartTime)189 public static void setProvisioningStartTimeMillis(Context context, 190 @CurrentTimeMillisLong long provisioningStartTime) { 191 getSharedPreferences(context).edit().putLong(KEY_PROVISIONING_START_TIME_MILLIS, 192 provisioningStartTime).apply(); 193 } 194 195 /** Get the suffix used for the notification channel */ 196 @WorkerThread getNotificationChannelIdSuffix(Context context)197 public static String getNotificationChannelIdSuffix(Context context) { 198 ThreadAsserts.assertWorkerThread("getNotificationChannelIdSuffix"); 199 return getSharedPreferences(context).getString(KEY_NOTIFICATION_CHANNEL_ID_SUFFIX, 200 /* defValue= */ ""); 201 } 202 203 /** Set the suffix used for the notification channel */ 204 @WorkerThread setNotificationChannelIdSuffix(Context context, @Nullable String notificationChannelSuffix)205 public static void setNotificationChannelIdSuffix(Context context, 206 @Nullable String notificationChannelSuffix) { 207 ThreadAsserts.assertWorkerThread("setNotificationChannelIdSuffix"); 208 getSharedPreferences(context).edit() 209 .putString(KEY_NOTIFICATION_CHANNEL_ID_SUFFIX, notificationChannelSuffix).apply(); 210 } 211 212 /** Check if SUW timed out. */ 213 @WorkerThread isSetupWizardTimedOut(Context context)214 public static boolean isSetupWizardTimedOut(Context context) { 215 ThreadAsserts.assertWorkerThread("isSetupWizardTimedOut"); 216 return getSharedPreferences(context).getBoolean(KEY_SUW_TIMED_OUT, false); 217 } 218 219 /** Set the provisioning start time */ setSetupWizardTimedOut(Context context)220 public static void setSetupWizardTimedOut(Context context) { 221 getSharedPreferences(context).edit().putBoolean(KEY_SUW_TIMED_OUT, true).apply(); 222 } 223 224 /** 225 * Clear all user parameters. 226 */ 227 @WorkerThread clear(Context context)228 public static void clear(Context context) { 229 ThreadAsserts.assertWorkerThread("clear"); 230 if (!Build.isDebuggable()) { 231 throw new SecurityException("Clear is not allowed in non-debuggable build!"); 232 } 233 // We want to keep the boot time in order to reschedule works/alarms when system clock 234 // changes. 235 long bootTime = UserParameters.getBootTimeMillis(context); 236 getSharedPreferences(context).edit().clear().commit(); 237 UserParameters.setBootTimeMillis(context, bootTime); 238 } 239 240 /** 241 * Dump the current value of user parameters for the user associated with the input context. 242 */ dump(Context context)243 public static void dump(Context context) { 244 Executors.newSingleThreadScheduledExecutor().submit(() -> { 245 LogUtil.d(TAG, String.format(Locale.US, 246 "Dumping UserParameters for user: %s ...\n" 247 + "%s: %s\n" // user_state: 248 + "%s: %s\n" // boot-time-mills: 249 + "%s: %s\n" // next-check-in-time-millis: 250 + "%s: %s\n" // resume-provision-time-millis: 251 + "%s: %s\n" // next-provision-failed-step-time-millis: 252 + "%s: %s\n" // reset-device-time-millis: 253 + "%s: %s\n" // days-left-until-reset: 254 + "%s: %s\n" // notification-channel-suffix: 255 + "%s: %s\n", // suw-timed-out: 256 context.getUser(), 257 KEY_PROVISION_STATE, getProvisionState(context), 258 KEY_BOOT_TIME_MILLS, getBootTimeMillis(context), 259 KEY_NEXT_CHECK_IN_TIME_MILLIS, getNextCheckInTimeMillis(context), 260 KEY_RESUME_PROVISION_TIME_MILLIS, getResumeProvisionTimeMillis(context), 261 KEY_NEXT_PROVISION_FAILED_STEP_TIME_MILLIS, 262 getNextProvisionFailedStepTimeMills(context), 263 KEY_RESET_DEVICE_TIME_MILLIS, getResetDeviceTimeMillis(context), 264 KEY_DAYS_LEFT_UNTIL_RESET, getDaysLeftUntilReset(context), 265 KEY_NOTIFICATION_CHANNEL_ID_SUFFIX, getNotificationChannelIdSuffix(context), 266 KEY_SUW_TIMED_OUT, isSetupWizardTimedOut(context) 267 )); 268 }); 269 } 270 } 271