1 /* 2 * Copyright (C) 2022 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.worker; 18 19 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 20 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; 21 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; 22 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; 23 24 import static com.android.devicelockcontroller.common.DeviceLockConstants.DeviceIdType.DEVICE_ID_TYPE_IMEI; 25 import static com.android.devicelockcontroller.common.DeviceLockConstants.DeviceIdType.DEVICE_ID_TYPE_MEID; 26 import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_ALLOW_DEBUGGING; 27 import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_MANDATORY_PROVISION; 28 import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_PROVISIONING_TYPE; 29 import static com.android.devicelockcontroller.common.DeviceLockConstants.READY_FOR_PROVISION; 30 import static com.android.devicelockcontroller.common.DeviceLockConstants.RETRY_CHECK_IN; 31 import static com.android.devicelockcontroller.common.DeviceLockConstants.STATUS_UNSPECIFIED; 32 import static com.android.devicelockcontroller.common.DeviceLockConstants.STOP_CHECK_IN; 33 import static com.android.devicelockcontroller.common.DeviceLockConstants.TOTAL_DEVICE_ID_TYPES; 34 import static com.android.devicelockcontroller.provision.worker.GetFcmTokenWorker.FCM_TOKEN_WORKER_BACKOFF_DELAY; 35 import static com.android.devicelockcontroller.provision.worker.GetFcmTokenWorker.FCM_TOKEN_WORKER_INITIAL_DELAY; 36 import static com.android.devicelockcontroller.provision.worker.GetFcmTokenWorker.FCM_TOKEN_WORK_NAME; 37 import static com.android.devicelockcontroller.receivers.CheckInBootCompletedReceiver.disableCheckInBootCompletedReceiver; 38 import static com.android.devicelockcontroller.stats.StatsLogger.CheckInRetryReason.CONFIG_UNAVAILABLE; 39 import static com.android.devicelockcontroller.stats.StatsLogger.CheckInRetryReason.NETWORK_TIME_UNAVAILABLE; 40 import static com.android.devicelockcontroller.stats.StatsLogger.CheckInRetryReason.RESPONSE_UNSPECIFIED; 41 42 import android.content.Context; 43 import android.content.Intent; 44 import android.content.pm.PackageManager; 45 import android.net.NetworkRequest; 46 import android.os.Bundle; 47 import android.os.SystemClock; 48 import android.os.UserHandle; 49 import android.telephony.TelephonyManager; 50 import android.util.ArraySet; 51 52 import androidx.annotation.NonNull; 53 import androidx.annotation.Nullable; 54 import androidx.annotation.VisibleForTesting; 55 import androidx.annotation.WorkerThread; 56 import androidx.work.BackoffPolicy; 57 import androidx.work.Constraints; 58 import androidx.work.ExistingWorkPolicy; 59 import androidx.work.NetworkType; 60 import androidx.work.OneTimeWorkRequest; 61 import androidx.work.WorkManager; 62 63 import com.android.devicelockcontroller.R; 64 import com.android.devicelockcontroller.common.DeviceId; 65 import com.android.devicelockcontroller.policy.PolicyObjectsProvider; 66 import com.android.devicelockcontroller.provision.grpc.GetDeviceCheckInStatusGrpcResponse; 67 import com.android.devicelockcontroller.provision.grpc.ProvisioningConfiguration; 68 import com.android.devicelockcontroller.receivers.ProvisionReadyReceiver; 69 import com.android.devicelockcontroller.schedule.DeviceLockControllerScheduler; 70 import com.android.devicelockcontroller.stats.StatsLogger; 71 import com.android.devicelockcontroller.stats.StatsLoggerProvider; 72 import com.android.devicelockcontroller.storage.GlobalParametersClient; 73 import com.android.devicelockcontroller.storage.SetupParametersClient; 74 import com.android.devicelockcontroller.util.LogUtil; 75 76 import com.google.common.base.Strings; 77 import com.google.common.util.concurrent.FutureCallback; 78 import com.google.common.util.concurrent.Futures; 79 import com.google.common.util.concurrent.ListenableFuture; 80 import com.google.common.util.concurrent.MoreExecutors; 81 82 import java.time.DateTimeException; 83 import java.time.Duration; 84 85 /** 86 * Helper class to perform the device check-in process with device lock backend server 87 */ 88 public final class DeviceCheckInHelper extends AbstractDeviceCheckInHelper { 89 private static final String TAG = "DeviceCheckInHelper"; 90 private final Context mAppContext; 91 private final TelephonyManager mTelephonyManager; 92 private final StatsLogger mStatsLogger; 93 DeviceCheckInHelper(Context appContext)94 public DeviceCheckInHelper(Context appContext) { 95 mAppContext = appContext; 96 mTelephonyManager = mAppContext.getSystemService(TelephonyManager.class); 97 mStatsLogger = ((StatsLoggerProvider) mAppContext).getStatsLogger(); 98 } 99 hasGsm()100 private boolean hasGsm() { 101 return mAppContext.getPackageManager().hasSystemFeature( 102 PackageManager.FEATURE_TELEPHONY_GSM); 103 } 104 hasCdma()105 private boolean hasCdma() { 106 return mAppContext.getPackageManager().hasSystemFeature( 107 PackageManager.FEATURE_TELEPHONY_CDMA); 108 } 109 110 @Override getDeviceUniqueIds()111 ArraySet<DeviceId> getDeviceUniqueIds() { 112 final int deviceIdTypeBitmap = mAppContext.getResources().getInteger( 113 R.integer.device_id_type_bitmap); 114 if (deviceIdTypeBitmap < 0) { 115 LogUtil.e(TAG, "getDeviceId: Cannot get device_id_type_bitmap"); 116 return new ArraySet<>(); 117 } 118 119 return getDeviceAvailableUniqueIds(deviceIdTypeBitmap); 120 } 121 122 @VisibleForTesting getDeviceAvailableUniqueIds(int deviceIdTypeBitmap)123 ArraySet<DeviceId> getDeviceAvailableUniqueIds(int deviceIdTypeBitmap) { 124 125 final int totalSlotCount = mTelephonyManager.getActiveModemCount(); 126 final int maximumIdCount = TOTAL_DEVICE_ID_TYPES * totalSlotCount; 127 final ArraySet<DeviceId> deviceIds = new ArraySet<>(maximumIdCount); 128 if (maximumIdCount == 0) return deviceIds; 129 130 for (int i = 0; i < totalSlotCount; i++) { 131 if (hasGsm() && (deviceIdTypeBitmap & (1 << DEVICE_ID_TYPE_IMEI)) != 0) { 132 final String imei = mTelephonyManager.getImei(i); 133 134 if (imei != null) { 135 deviceIds.add(new DeviceId(DEVICE_ID_TYPE_IMEI, imei)); 136 } 137 } 138 139 if (hasCdma() && (deviceIdTypeBitmap & (1 << DEVICE_ID_TYPE_MEID)) != 0) { 140 final String meid = mTelephonyManager.getMeid(i); 141 142 if (meid != null) { 143 deviceIds.add(new DeviceId(DEVICE_ID_TYPE_MEID, meid)); 144 } 145 } 146 } 147 148 return deviceIds; 149 } 150 151 @Override getCarrierInfo()152 String getCarrierInfo() { 153 return mTelephonyManager.getSimOperator(); 154 } 155 156 @Override 157 @WorkerThread handleGetDeviceCheckInStatusResponse( GetDeviceCheckInStatusGrpcResponse response, DeviceLockControllerScheduler scheduler, @Nullable String fcmRegistrationToken)158 boolean handleGetDeviceCheckInStatusResponse( 159 GetDeviceCheckInStatusGrpcResponse response, 160 DeviceLockControllerScheduler scheduler, 161 @Nullable String fcmRegistrationToken) { 162 Futures.getUnchecked(GlobalParametersClient.getInstance().setRegisteredDeviceId( 163 response.getRegisteredDeviceIdentifier())); 164 LogUtil.d(TAG, "check in response: " + response.getDeviceCheckInStatus()); 165 switch (response.getDeviceCheckInStatus()) { 166 case READY_FOR_PROVISION: 167 boolean result = handleProvisionReadyResponse(response); 168 disableCheckInBootCompletedReceiver(mAppContext); 169 maybeEnqueueFcmRegistrationTokenRetrievalWork(fcmRegistrationToken); 170 return result; 171 case RETRY_CHECK_IN: 172 try { 173 Duration delay = Duration.between( 174 SystemClock.currentNetworkTimeClock().instant(), 175 response.getNextCheckInTime()); 176 // Retry immediately if next check in time is in the past. 177 delay = delay.isNegative() ? Duration.ZERO : delay; 178 scheduler.scheduleRetryCheckInWork(delay); 179 maybeEnqueueFcmRegistrationTokenRetrievalWork(fcmRegistrationToken); 180 return true; 181 } catch (DateTimeException e) { 182 LogUtil.e(TAG, "No network time is available!"); 183 mStatsLogger.logCheckInRetry(NETWORK_TIME_UNAVAILABLE); 184 return false; 185 } 186 case STOP_CHECK_IN: 187 final ListenableFuture<Void> clearRestrictionsFuture = 188 ((PolicyObjectsProvider) mAppContext).getFinalizationController() 189 .finalizeNotEnrolledDevice(); 190 Futures.addCallback(clearRestrictionsFuture, 191 new FutureCallback<>() { 192 @Override 193 public void onSuccess(Void result) { 194 // no-op 195 } 196 197 @Override 198 public void onFailure(Throwable t) { 199 LogUtil.e(TAG, "Failed to finalize device", t); 200 } 201 }, MoreExecutors.directExecutor() 202 ); 203 return true; 204 case STATUS_UNSPECIFIED: 205 default: 206 mStatsLogger.logCheckInRetry(RESPONSE_UNSPECIFIED); 207 return false; 208 } 209 } 210 211 /** 212 * Starts a job to retrieve the FCM registration token later if the current one used to\ 213 * check-in is invalid. 214 * 215 * @param fcmRegistrationToken the current token 216 */ maybeEnqueueFcmRegistrationTokenRetrievalWork( @ullable String fcmRegistrationToken)217 private void maybeEnqueueFcmRegistrationTokenRetrievalWork( 218 @Nullable String fcmRegistrationToken) { 219 if (Strings.isNullOrEmpty(fcmRegistrationToken) || fcmRegistrationToken.isBlank()) { 220 NetworkRequest request = new NetworkRequest.Builder() 221 .addCapability(NET_CAPABILITY_NOT_RESTRICTED) 222 .addCapability(NET_CAPABILITY_TRUSTED) 223 .addCapability(NET_CAPABILITY_INTERNET) 224 .addCapability(NET_CAPABILITY_NOT_VPN) 225 .build(); 226 OneTimeWorkRequest.Builder builder = 227 new OneTimeWorkRequest.Builder(GetFcmTokenWorker.class) 228 .setConstraints( 229 new Constraints.Builder().setRequiredNetworkRequest(request, 230 NetworkType.CONNECTED).build()) 231 .setInitialDelay(FCM_TOKEN_WORKER_INITIAL_DELAY) 232 .setBackoffCriteria( 233 BackoffPolicy.EXPONENTIAL, FCM_TOKEN_WORKER_BACKOFF_DELAY); 234 235 WorkManager.getInstance(mAppContext).enqueueUniqueWork(FCM_TOKEN_WORK_NAME, 236 ExistingWorkPolicy.REPLACE, builder.build()); 237 } 238 } 239 240 @VisibleForTesting 241 @WorkerThread handleProvisionReadyResponse( @onNull GetDeviceCheckInStatusGrpcResponse response)242 boolean handleProvisionReadyResponse( 243 @NonNull GetDeviceCheckInStatusGrpcResponse response) { 244 GlobalParametersClient globalParametersClient = GlobalParametersClient.getInstance(); 245 Futures.getUnchecked(globalParametersClient.setProvisionForced( 246 response.isProvisionForced())); 247 final ProvisioningConfiguration configuration = response.getProvisioningConfig(); 248 if (configuration == null) { 249 LogUtil.e(TAG, "Provisioning Configuration is not provided by server!"); 250 mStatsLogger.logCheckInRetry(CONFIG_UNAVAILABLE); 251 return false; 252 } 253 final Bundle provisionBundle = configuration.toBundle(); 254 provisionBundle.putInt(EXTRA_PROVISIONING_TYPE, response.getProvisioningType()); 255 provisionBundle.putBoolean(EXTRA_MANDATORY_PROVISION, 256 response.isProvisioningMandatory()); 257 provisionBundle.putBoolean(EXTRA_ALLOW_DEBUGGING, response.isDebuggingAllowed()); 258 Futures.getUnchecked( 259 SetupParametersClient.getInstance().createPrefs(provisionBundle)); 260 Futures.getUnchecked(globalParametersClient.setProvisionReady(true)); 261 mAppContext.sendBroadcastAsUser( 262 new Intent(mAppContext, ProvisionReadyReceiver.class), 263 UserHandle.ALL); 264 return true; 265 } 266 } 267