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