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.debug; 18 19 import static com.android.devicelockcontroller.common.DeviceLockConstants.DeviceProvisionState.PROVISION_STATE_UNSPECIFIED; 20 import static com.android.devicelockcontroller.common.DeviceLockConstants.READY_FOR_PROVISION; 21 22 import android.content.Context; 23 import android.content.SharedPreferences; 24 import android.os.SystemClock; 25 import android.util.ArraySet; 26 27 import androidx.annotation.Keep; 28 import androidx.annotation.NonNull; 29 import androidx.annotation.Nullable; 30 31 import com.android.devicelockcontroller.common.DeviceId; 32 import com.android.devicelockcontroller.common.DeviceLockConstants.DeviceCheckInStatus; 33 import com.android.devicelockcontroller.common.DeviceLockConstants.DeviceProvisionState; 34 import com.android.devicelockcontroller.common.DeviceLockConstants.ProvisionFailureReason; 35 import com.android.devicelockcontroller.common.DeviceLockConstants.ProvisioningType; 36 import com.android.devicelockcontroller.provision.grpc.DeviceCheckInClient; 37 import com.android.devicelockcontroller.provision.grpc.GetDeviceCheckInStatusGrpcResponse; 38 import com.android.devicelockcontroller.provision.grpc.IsDeviceInApprovedCountryGrpcResponse; 39 import com.android.devicelockcontroller.provision.grpc.PauseDeviceProvisioningGrpcResponse; 40 import com.android.devicelockcontroller.provision.grpc.ProvisioningConfiguration; 41 import com.android.devicelockcontroller.provision.grpc.ReportDeviceProvisionStateGrpcResponse; 42 import com.android.devicelockcontroller.provision.grpc.UpdateFcmTokenGrpcResponse; 43 import com.android.devicelockcontroller.util.LogUtil; 44 import com.android.devicelockcontroller.util.ThreadAsserts; 45 46 import java.time.Duration; 47 import java.time.Instant; 48 import java.util.List; 49 50 /** 51 * An implementation of the {@link DeviceCheckInClient} which simulate server responses by 52 * reading it from local storage. 53 */ 54 @Keep 55 public final class DeviceCheckInClientDebug extends DeviceCheckInClient { 56 57 public static final String TAG = "DeviceCheckInClientDebug"; 58 public static final String DEBUG_DEVICELOCK_CHECKIN_STATUS = "debug.devicelock.checkin.status"; 59 public static final String DEBUG_DEVICELOCK_CHECKIN_RETRY_DELAY = 60 "debug.devicelock.checkin.retry-delay"; 61 public static final String DEBUG_DEVICELOCK_CHECKIN_FORCE_PROVISIONING = 62 "debug.devicelock.checkin.force-provisioning"; 63 public static final String DEBUG_DEVICELOCK_CHECKIN_APPROVED_COUNTRY = 64 "debug.devicelock.checkin.approved-country"; 65 public static final String DEBUG_DEVICELOCK_CHECKIN_NEXT_PROVISION_STATE = 66 "debug.devicelock.checkin.next-provision-state"; 67 public static final String DEBUG_DEVICELOCK_CHECKIN_DAYS_LEFT_UNTIL_RESET = 68 "debug.devicelock.checkin.days-left-until-reset"; 69 setDebugClientEnabled(Context context, boolean enabled)70 static void setDebugClientEnabled(Context context, boolean enabled) { 71 getSharedPreferences(context).edit().putBoolean(DEBUG_DEVICELOCK_CHECKIN, enabled).apply(); 72 } 73 setDebugCheckInStatus(Context context, @DeviceCheckInStatus int status)74 static void setDebugCheckInStatus(Context context, @DeviceCheckInStatus int status) { 75 getSharedPreferences(context).edit().putInt(DEBUG_DEVICELOCK_CHECKIN_STATUS, 76 status).apply(); 77 } 78 setDebugForceProvisioning(Context context, boolean isForced)79 static void setDebugForceProvisioning(Context context, boolean isForced) { 80 getSharedPreferences(context).edit().putBoolean(DEBUG_DEVICELOCK_CHECKIN_FORCE_PROVISIONING, 81 isForced).apply(); 82 } 83 setDebugCheckInRetryDelay(Context context, int delayMinute)84 static void setDebugCheckInRetryDelay(Context context, int delayMinute) { 85 getSharedPreferences(context).edit().putInt(DEBUG_DEVICELOCK_CHECKIN_RETRY_DELAY, 86 delayMinute).apply(); 87 } 88 setDebugApprovedCountry(Context context, boolean isInApprovedCountry)89 static void setDebugApprovedCountry(Context context, 90 boolean isInApprovedCountry) { 91 getSharedPreferences(context).edit().putBoolean(DEBUG_DEVICELOCK_CHECKIN_APPROVED_COUNTRY, 92 isInApprovedCountry).apply(); 93 } 94 setDebugNextProvisionState( Context context, @DeviceProvisionState int provisionState)95 static void setDebugNextProvisionState( 96 Context context, @DeviceProvisionState int provisionState) { 97 getSharedPreferences(context).edit().putInt(DEBUG_DEVICELOCK_CHECKIN_NEXT_PROVISION_STATE, 98 provisionState).apply(); 99 } 100 setDebugDaysLeftUntilReset(Context context, int daysLeftUntilReset)101 static void setDebugDaysLeftUntilReset(Context context, 102 int daysLeftUntilReset) { 103 getSharedPreferences(context).edit().putInt(DEBUG_DEVICELOCK_CHECKIN_DAYS_LEFT_UNTIL_RESET, 104 daysLeftUntilReset).apply(); 105 } 106 dumpDebugCheckInClientResponses(Context context)107 static void dumpDebugCheckInClientResponses(Context context) { 108 LogUtil.d(TAG, 109 "Current Debug Client Responses:\n" + getSharedPreferences(context).getAll()); 110 } 111 clear(Context context)112 static void clear(Context context) { 113 getSharedPreferences(context).edit().clear().apply(); 114 } 115 getSharedPreference(String key, T defValue)116 private static <T> T getSharedPreference(String key, T defValue) { 117 SharedPreferences preferences = getSharedPreferences(/* context= */ null); 118 if (preferences == null) return defValue; 119 T value = (T) preferences.getAll().get(key); 120 return value == null ? defValue : value; 121 } 122 123 /** Check In with DeviceLock backend server and get the next step for the device. */ 124 @Override getDeviceCheckInStatus( ArraySet<DeviceId> deviceIds, String carrierInfo, String deviceLocale, long deviceLockApexVersion, @Nullable String fcmRegistrationToken)125 public GetDeviceCheckInStatusGrpcResponse getDeviceCheckInStatus( 126 ArraySet<DeviceId> deviceIds, 127 String carrierInfo, 128 String deviceLocale, 129 long deviceLockApexVersion, 130 @Nullable String fcmRegistrationToken) { 131 ThreadAsserts.assertWorkerThread("getDeviceCheckInStatus"); 132 return new GetDeviceCheckInStatusGrpcResponse() { 133 @Override 134 @DeviceCheckInStatus 135 public int getDeviceCheckInStatus() { 136 return DebugLogUtil.logAndReturn(TAG, 137 getSharedPreference(DEBUG_DEVICELOCK_CHECKIN_STATUS, READY_FOR_PROVISION)); 138 } 139 140 @Nullable 141 @Override 142 public String getRegisteredDeviceIdentifier() { 143 return DebugLogUtil.logAndReturn(TAG, 144 !deviceIds.isEmpty() ? deviceIds.valueAt(0).getId() : null); 145 } 146 147 @Nullable 148 @Override 149 public Instant getNextCheckInTime() { 150 Duration delay = Duration.ofMinutes( 151 getSharedPreference(DEBUG_DEVICELOCK_CHECKIN_RETRY_DELAY, /* def= */ 1)); 152 return DebugLogUtil.logAndReturn(TAG, 153 SystemClock.currentNetworkTimeClock().instant().plus(delay)); 154 } 155 156 @Nullable 157 @Override 158 public ProvisioningConfiguration getProvisioningConfig() { 159 // This should be overridden using SetupParametersOverrider. 160 return new ProvisioningConfiguration( 161 /* kioskAppProviderName= */ "", 162 /* kioskAppPackageName= */ "", 163 /* kioskAppAllowlistPackages= */ List.of(""), 164 /* kioskAppEnableOutgoingCalls= */ false, 165 /* kioskAppEnableEnableNotifications= */ false, 166 /* disallowInstallingFromUnknownSources= */ false, 167 /* termsAndConditionsUrl= */ "", 168 /* supportUrl= */ ""); 169 } 170 171 @Override 172 public @ProvisioningType int getProvisioningType() { 173 // This should be overridden using SetupParametersOverrider. 174 return ProvisioningType.TYPE_UNDEFINED; 175 } 176 177 @Override 178 public boolean isProvisioningMandatory() { 179 // This should be overridden using SetupParametersOverrider. 180 return false; 181 } 182 183 @Override 184 public boolean isProvisionForced() { 185 return DebugLogUtil.logAndReturn(TAG, 186 getSharedPreference(DEBUG_DEVICELOCK_CHECKIN_FORCE_PROVISIONING, false)); 187 } 188 189 @Override 190 public boolean isDeviceInApprovedCountry() { 191 return DebugLogUtil.logAndReturn(TAG, 192 getSharedPreference(DEBUG_DEVICELOCK_CHECKIN_APPROVED_COUNTRY, true)); 193 } 194 195 @Override 196 public boolean isDebuggingAllowed() { 197 // This value should not matter since the device is already on a debug build and 198 // can use debugging. 199 return true; 200 } 201 }; 202 } 203 204 /** 205 * Check if the device is in an approved country for the device lock program. 206 */ 207 @Override 208 public IsDeviceInApprovedCountryGrpcResponse isDeviceInApprovedCountry( 209 @Nullable String carrierInfo) { 210 ThreadAsserts.assertWorkerThread("isDeviceInApprovedCountry"); 211 return new IsDeviceInApprovedCountryGrpcResponse() { 212 @Override 213 public boolean isDeviceInApprovedCountry() { 214 return DebugLogUtil.logAndReturn(TAG, 215 getSharedPreference(DEBUG_DEVICELOCK_CHECKIN_APPROVED_COUNTRY, true)); 216 } 217 }; 218 } 219 220 /** 221 * Inform the server that device provisioning has been paused for a certain amount of time. 222 */ 223 @Override 224 public PauseDeviceProvisioningGrpcResponse pauseDeviceProvisioning(int reason) { 225 ThreadAsserts.assertWorkerThread("pauseDeviceProvisioning"); 226 return new PauseDeviceProvisioningGrpcResponse(); 227 } 228 229 /** 230 * Reports the current provision state of the device. 231 */ 232 @Override 233 public ReportDeviceProvisionStateGrpcResponse reportDeviceProvisionState( 234 int lastReceivedProvisionState, boolean isSuccessful, 235 @ProvisionFailureReason int reason) { 236 ThreadAsserts.assertWorkerThread("reportDeviceProvisionState"); 237 return new ReportDeviceProvisionStateGrpcResponse() { 238 @Override 239 @DeviceProvisionState 240 public int getNextClientProvisionState() { 241 return DebugLogUtil.logAndReturn(TAG, getSharedPreference( 242 DEBUG_DEVICELOCK_CHECKIN_NEXT_PROVISION_STATE, 243 PROVISION_STATE_UNSPECIFIED)); 244 } 245 246 @Override 247 public int getDaysLeftUntilReset() { 248 return DebugLogUtil.logAndReturn(TAG, 249 getSharedPreference( 250 DEBUG_DEVICELOCK_CHECKIN_DAYS_LEFT_UNTIL_RESET, /* def= */ 1)); 251 } 252 }; 253 } 254 255 @Override 256 public UpdateFcmTokenGrpcResponse updateFcmToken(ArraySet<DeviceId> deviceIds, 257 @NonNull String fcmRegistrationToken) { 258 ThreadAsserts.assertWorkerThread("updateFcmToken"); 259 return new UpdateFcmTokenGrpcResponse() { 260 @Override 261 public int getFcmTokenResult() { 262 return FcmTokenResult.RESULT_SUCCESS; 263 } 264 }; 265 } 266 } 267