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