• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.provision.grpc;
18 
19 import android.content.Context;
20 import android.content.SharedPreferences;
21 import android.net.ConnectivityManager;
22 import android.os.Build;
23 import android.os.UserHandle;
24 import android.util.ArraySet;
25 
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 import androidx.annotation.WorkerThread;
29 
30 import com.android.devicelockcontroller.common.DeviceId;
31 import com.android.devicelockcontroller.common.DeviceLockConstants.DeviceProvisionState;
32 import com.android.devicelockcontroller.common.DeviceLockConstants.PauseDeviceProvisioningReason;
33 import com.android.devicelockcontroller.common.DeviceLockConstants.ProvisionFailureReason;
34 import com.android.devicelockcontroller.provision.grpc.impl.DeviceCheckInClientImpl;
35 import com.android.devicelockcontroller.util.LogUtil;
36 
37 import io.grpc.ClientInterceptor;
38 
39 /**
40  * An abstract class that's intended for implementation of class that manages communication with
41  * DeviceLock backend server.
42  */
43 public abstract class DeviceCheckInClient {
44     private static final String TAG = "DeviceCheckInClient";
45     private static final String FILENAME = "debug-check-in-preferences";
46 
47     public static final String DEVICE_CHECK_IN_CLIENT_DEBUG_CLASS_NAME =
48             "com.android.devicelockcontroller.debug.DeviceCheckInClientDebug";
49     protected static final String DEBUG_DEVICELOCK_CHECKIN = "debug.devicelock.checkin";
50     private static final String HOST_NAME_OVERRIDE = "host.name.override";
51     private static volatile DeviceCheckInClient sClient;
52 
53     @Nullable
54     protected static String sRegisteredId;
55     protected static String sHostName = "";
56     protected static int sPortNumber = 0;
57     private static volatile boolean sUseDebugClient;
58 
59     @Nullable
60     private static volatile SharedPreferences sSharedPreferences;
61 
62     @Nullable
getSharedPreferences( @ullable Context context)63     protected static synchronized SharedPreferences getSharedPreferences(
64             @Nullable Context context) {
65         if (sSharedPreferences == null && context != null) {
66             sSharedPreferences =
67                     context.createContextAsUser(UserHandle.SYSTEM, /* flags= */
68                             0).createDeviceProtectedStorageContext().getSharedPreferences(FILENAME,
69                             Context.MODE_PRIVATE);
70         }
71         return sSharedPreferences;
72     }
73 
74     /**
75      * Override the host name so that the client always connects to it instead
76      */
setHostNameOverride(Context context, String override)77     public static void setHostNameOverride(Context context, String override) {
78         getSharedPreferences(context).edit().putString(HOST_NAME_OVERRIDE, override).apply();
79     }
80 
81     /**
82      * Get an instance of DeviceCheckInClient object.
83      */
getInstance( Context context, String hostName, int portNumber, ClientInterceptor clientInterceptor, @Nullable String registeredId)84     public static DeviceCheckInClient getInstance(
85             Context context,
86             String hostName,
87             int portNumber,
88             ClientInterceptor clientInterceptor,
89             @Nullable String registeredId) {
90         boolean useDebugClient = false;
91         String hostNameOverride = "";
92         if (Build.isDebuggable()) {
93             useDebugClient = getSharedPreferences(context).getBoolean(
94                     DEBUG_DEVICELOCK_CHECKIN, /* def= */ false);
95             hostNameOverride = getSharedPreferences(context).getString(
96                     HOST_NAME_OVERRIDE, /* def= */ "");
97             if (!hostNameOverride.isEmpty()) {
98                 hostName = hostNameOverride;
99             }
100         }
101         synchronized (DeviceCheckInClient.class) {
102             try {
103                 boolean createRequired =
104                         (sClient == null || sUseDebugClient != useDebugClient)
105                                 || (registeredId != null && !registeredId.equals(sRegisteredId))
106                                 || (hostName != null && !hostName.equals(sHostName));
107 
108                 if (createRequired) {
109                     if (sClient != null) {
110                         sClient.cleanUp();
111                     }
112                     sHostName = hostName;
113                     sPortNumber = portNumber;
114                     sRegisteredId = registeredId;
115                     sUseDebugClient = useDebugClient;
116                     if (Build.isDebuggable() && sUseDebugClient) {
117                         final String className = DEVICE_CHECK_IN_CLIENT_DEBUG_CLASS_NAME;
118                         LogUtil.d(TAG, "Creating instance for " + className);
119                         Class<?> clazz = Class.forName(className);
120                         sClient =
121                                 (DeviceCheckInClient) clazz.getDeclaredConstructor().newInstance();
122                     } else {
123                         sClient = new DeviceCheckInClientImpl(clientInterceptor,
124                                 context.getSystemService(ConnectivityManager.class));
125                     }
126                 }
127             } catch (Exception e) {
128                 throw new RuntimeException("Failed to get DeviceCheckInClient instance", e);
129             }
130         }
131         return sClient;
132     }
133 
134     /**
135      * Check In with DeviceLock backend server and get the next step for the device
136      *
137      * @param deviceIds            A set of all device unique identifiers, this could include IMEIs,
138      *                             MEIDs, etc.
139      * @param carrierInfo          The information of the device's sim operator which is used to
140      *                             determine the device's geological location and eventually
141      *                             eligibility of the DeviceLock program.
142      * @param deviceLocale         The locale of the device.
143      * @param deviceLockApexVersion The version of the device lock apex.
144      * @param fcmRegistrationToken The fcm registration token
145      * @return A class that encapsulate the response from the backend server.
146      */
147     @WorkerThread
getDeviceCheckInStatus( ArraySet<DeviceId> deviceIds, String carrierInfo, String deviceLocale, long deviceLockApexVersion, @Nullable String fcmRegistrationToken)148     public abstract GetDeviceCheckInStatusGrpcResponse getDeviceCheckInStatus(
149             ArraySet<DeviceId> deviceIds, String carrierInfo,
150             String deviceLocale, long deviceLockApexVersion,
151             @Nullable String fcmRegistrationToken);
152 
153     /**
154      * Check if the device is in an approved country for the device lock program.
155      *
156      * @param carrierInfo The information of the device's sim operator which is used to determine
157      *                    the device's geological location and eventually eligibility of the
158      *                    DeviceLock program. Could be null if unavailable.
159      * @return A class that encapsulate the response from the backend server.
160      */
161     @WorkerThread
isDeviceInApprovedCountry( @ullable String carrierInfo)162     public abstract IsDeviceInApprovedCountryGrpcResponse isDeviceInApprovedCountry(
163             @Nullable String carrierInfo);
164 
165     /**
166      * Inform the server that device provisioning has been paused for a certain amount of time.
167      *
168      * @param reason The reason that provisioning has been paused.
169      * @return A class that encapsulate the response from the backend sever.
170      */
171     @WorkerThread
pauseDeviceProvisioning( @auseDeviceProvisioningReason int reason)172     public abstract PauseDeviceProvisioningGrpcResponse pauseDeviceProvisioning(
173             @PauseDeviceProvisioningReason int reason);
174 
175     /**
176      * Reports the current provision state of the device.
177      *
178      * @param lastReceivedProvisionState one of {@link DeviceProvisionState}.
179      *                                   It must be the value from the response when this API
180      *                                   was called last time. If this API is called for the first
181      *                                   time, then
182      *                                   {@link
183      *                                   DeviceProvisionState#PROVISION_STATE_UNSPECIFIED }
184      *                                   must be used.
185      * @param isSuccessful               true if the device has been setup for DeviceLock program
186      *                                   successful; false otherwise.
187      * @return A class that encapsulate the response from the backend server.
188      */
189     @WorkerThread
reportDeviceProvisionState( @eviceProvisionState int lastReceivedProvisionState, boolean isSuccessful, @ProvisionFailureReason int failureReason)190     public abstract ReportDeviceProvisionStateGrpcResponse reportDeviceProvisionState(
191             @DeviceProvisionState int lastReceivedProvisionState,
192             boolean isSuccessful, @ProvisionFailureReason int failureReason);
193 
194     /**
195      * Update FCM registration token on device lock backend server for the given device identifiers.
196      *
197      * @param deviceIds            A set of all device unique identifiers, this could include IMEIs,
198      *                             MEIDs, etc.
199      * @param fcmRegistrationToken The fcm registration token
200      * @return A class that encapsulate the response from the backend server.
201      */
202     @WorkerThread
updateFcmToken( ArraySet<DeviceId> deviceIds, @NonNull String fcmRegistrationToken)203     public abstract UpdateFcmTokenGrpcResponse updateFcmToken(
204             ArraySet<DeviceId> deviceIds,
205             @NonNull String fcmRegistrationToken);
206 
207     /**
208      * Called when this device check in client is no longer in use and should clean up its
209      * resources.
210      */
cleanUp()211     public void cleanUp() {};
212 }
213