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.rkpdapp.metrics; 18 19 import android.content.Context; 20 import android.hardware.security.keymint.IRemotelyProvisionedComponent; 21 import android.net.ConnectivityManager; 22 import android.net.NetworkCapabilities; 23 import android.os.SystemClock; 24 import android.os.SystemProperties; 25 import android.util.Log; 26 27 import com.android.rkpdapp.service.RemoteProvisioningService; 28 import com.android.rkpdapp.utils.StopWatch; 29 30 import java.time.Duration; 31 32 /** 33 * Contains the metrics values that are recorded for every attempt to remotely provision keys. 34 * This class will automatically push the atoms on close, and is intended to be used with a 35 * try-with-resources block to ensure metrics are automatically logged on completion of an attempt. 36 */ 37 public final class ProvisioningAttempt implements AutoCloseable { 38 // The state of remote provisioning enablement 39 public enum Enablement { 40 UNKNOWN, 41 ENABLED_WITH_FALLBACK, 42 ENABLED_RKP_ONLY, 43 DISABLED 44 } 45 46 public enum Status { 47 UNKNOWN, 48 KEYS_SUCCESSFULLY_PROVISIONED, 49 NO_PROVISIONING_NEEDED, 50 PROVISIONING_DISABLED, 51 INTERNAL_ERROR, 52 NO_NETWORK_CONNECTIVITY, 53 OUT_OF_ERROR_BUDGET, 54 INTERRUPTED, 55 GENERATE_KEYPAIR_FAILED, 56 GENERATE_CSR_FAILED, 57 GET_POOL_STATUS_FAILED, 58 INSERT_CHAIN_INTO_POOL_FAILED, 59 FETCH_GEEK_TIMED_OUT, 60 FETCH_GEEK_IO_EXCEPTION, 61 FETCH_GEEK_HTTP_ERROR, 62 SIGN_CERTS_TIMED_OUT, 63 SIGN_CERTS_IO_EXCEPTION, 64 SIGN_CERTS_HTTP_ERROR, 65 SIGN_CERTS_DEVICE_NOT_REGISTERED 66 } 67 68 private static final String TAG = RemoteProvisioningService.TAG; 69 70 private final Context mContext; 71 private final int mCause; 72 private final StopWatch mServerWaitTimer = new StopWatch(TAG); 73 private final StopWatch mBinderWaitTimer = new StopWatch(TAG); 74 private final StopWatch mLockWaitTimer = new StopWatch(TAG); 75 private final StopWatch mTotalTimer = new StopWatch(TAG); 76 private final String mRemotelyProvisionedComponent; 77 private Enablement mEnablement; 78 private boolean mIsKeyPoolEmpty = false; 79 private Status mStatus = Status.UNKNOWN; 80 private int mHttpStatusError; 81 private String mRootCertFingerprint = "<none>"; 82 private int mCertChainLength; 83 ProvisioningAttempt(Context context, int cause, String remotelyProvisionedComponent, Enablement enablement)84 private ProvisioningAttempt(Context context, int cause, 85 String remotelyProvisionedComponent, Enablement enablement) { 86 mContext = context; 87 mCause = cause; 88 mRemotelyProvisionedComponent = remotelyProvisionedComponent; 89 mEnablement = enablement; 90 mTotalTimer.start(); 91 } 92 93 /** Start collecting metrics for scheduled provisioning. */ createScheduledAttemptMetrics(Context context)94 public static ProvisioningAttempt createScheduledAttemptMetrics(Context context) { 95 // Scheduled jobs (PeriodicProvisioner) intermix a lot of operations for multiple 96 // components, which makes it difficult to tease apart what is happening for which 97 // remotely provisioned component. Thus, on these calls, the component and 98 // component-specific enablement are not logged. 99 return new ProvisioningAttempt( 100 context, 101 RkpdStatsLog.REMOTE_KEY_PROVISIONING_ATTEMPT__CAUSE__SCHEDULED, 102 "", 103 Enablement.UNKNOWN); 104 } 105 106 /** Start collecting metrics when an attestation key has been consumed from the pool. */ createKeyConsumedAttemptMetrics(Context context, String remotelyProvisionedComponent)107 public static ProvisioningAttempt createKeyConsumedAttemptMetrics(Context context, 108 String remotelyProvisionedComponent) { 109 return new ProvisioningAttempt( 110 context, 111 RkpdStatsLog.REMOTE_KEY_PROVISIONING_ATTEMPT__CAUSE__KEY_CONSUMED, 112 remotelyProvisionedComponent, 113 getEnablementForComponent(remotelyProvisionedComponent)); 114 } 115 116 /** Start collecting metrics when the spare attestation key pool is empty. */ createOutOfKeysAttemptMetrics(Context context, String remotelyProvisionedComponent)117 public static ProvisioningAttempt createOutOfKeysAttemptMetrics(Context context, 118 String remotelyProvisionedComponent) { 119 return new ProvisioningAttempt( 120 context, 121 RkpdStatsLog.REMOTE_KEY_PROVISIONING_ATTEMPT__CAUSE__OUT_OF_KEYS, 122 remotelyProvisionedComponent, 123 getEnablementForComponent(remotelyProvisionedComponent)); 124 } 125 126 /** Record the state of RKP configuration. */ setEnablement(Enablement enablement)127 public void setEnablement(Enablement enablement) { 128 mEnablement = enablement; 129 } 130 131 /** Set to true if the provisioning encountered an empty key pool. */ setIsKeyPoolEmpty(boolean isEmpty)132 public void setIsKeyPoolEmpty(boolean isEmpty) { 133 mIsKeyPoolEmpty = isEmpty; 134 } 135 136 /** Set the status for this provisioning attempt. */ setStatus(Status status)137 public void setStatus(Status status) { 138 mStatus = status; 139 } 140 141 /** Set the last HTTP status encountered. */ setHttpStatusError(int httpStatusError)142 public void setHttpStatusError(int httpStatusError) { 143 mHttpStatusError = httpStatusError; 144 } 145 setRootCertFingerprint(String rootCertFingerprint)146 public void setRootCertFingerprint(String rootCertFingerprint) { 147 mRootCertFingerprint = rootCertFingerprint; 148 } 149 setCertChainLength(int certChainLength)150 public void setCertChainLength(int certChainLength) { 151 mCertChainLength = certChainLength; 152 } 153 154 /** 155 * Starts the server wait timer, returning a reference to an object to be closed when the 156 * wait is over. 157 */ startServerWait()158 public StopWatch startServerWait() { 159 mServerWaitTimer.start(); 160 return mServerWaitTimer; 161 } 162 163 /** 164 * Starts the binder wait timer, returning a reference to an object to be closed when the 165 * wait is over. 166 */ startBinderWait()167 public StopWatch startBinderWait() { 168 mBinderWaitTimer.start(); 169 return mBinderWaitTimer; 170 } 171 172 /** 173 * Starts the lock wait timer, returning a reference to an object to be closed when the 174 * wait is over. 175 */ startLockWait()176 public StopWatch startLockWait() { 177 mLockWaitTimer.start(); 178 return mLockWaitTimer; 179 } 180 181 /** Record the atoms for this metrics object. */ 182 @Override close()183 public void close() { 184 mTotalTimer.stop(); 185 186 int transportType = getTransportTypeForActiveNetwork(); 187 RkpdStatsLog.write(RkpdStatsLog.REMOTE_KEY_PROVISIONING_ATTEMPT, 188 mCause, mRemotelyProvisionedComponent, getUpTimeBucket(), getIntEnablement(), 189 mIsKeyPoolEmpty, getIntStatus(), mRootCertFingerprint, mCertChainLength); 190 RkpdStatsLog.write( 191 RkpdStatsLog.REMOTE_KEY_PROVISIONING_NETWORK_INFO, 192 transportType, getIntStatus(), mHttpStatusError); 193 RkpdStatsLog.write(RkpdStatsLog.REMOTE_KEY_PROVISIONING_TIMING, 194 mServerWaitTimer.getElapsedMillis(), mBinderWaitTimer.getElapsedMillis(), 195 mLockWaitTimer.getElapsedMillis(), mTotalTimer.getElapsedMillis(), transportType, 196 mRemotelyProvisionedComponent, mCause, getIntStatus()); 197 } 198 getEnablementForComponent(String serviceName)199 private static Enablement getEnablementForComponent(String serviceName) { 200 if (serviceName.equals(IRemotelyProvisionedComponent.DESCRIPTOR + "/default")) { 201 return readRkpOnlyProperty("remote_provisioning.tee.rkp_only"); 202 } 203 204 if (serviceName.equals(IRemotelyProvisionedComponent.DESCRIPTOR + "/strongbox")) { 205 return readRkpOnlyProperty("remote_provisioning.strongbox.rkp_only"); 206 } 207 208 Log.w(TAG, "Unknown remotely provisioned component name: " + serviceName); 209 return Enablement.UNKNOWN; 210 } 211 readRkpOnlyProperty(String property)212 private static Enablement readRkpOnlyProperty(String property) { 213 if (SystemProperties.getBoolean(property, false)) { 214 return Enablement.ENABLED_RKP_ONLY; 215 } 216 return Enablement.ENABLED_WITH_FALLBACK; 217 } 218 getTransportTypeForActiveNetwork()219 private int getTransportTypeForActiveNetwork() { 220 ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); 221 if (cm == null) { 222 Log.w(TAG, "Unable to get ConnectivityManager instance"); 223 return RkpdStatsLog 224 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_UNKNOWN; 225 } 226 227 NetworkCapabilities capabilities = cm.getNetworkCapabilities(cm.getActiveNetwork()); 228 if (capabilities == null) { 229 return RkpdStatsLog 230 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_UNKNOWN; 231 } 232 233 if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { 234 if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) 235 && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { 236 return RkpdStatsLog 237 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_WIFI_CELLULAR_VPN; 238 } 239 if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { 240 return RkpdStatsLog 241 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_CELLULAR_VPN; 242 } 243 if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { 244 return RkpdStatsLog 245 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_WIFI_VPN; 246 } 247 if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) { 248 return RkpdStatsLog 249 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_BLUETOOTH_VPN; 250 } 251 if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { 252 return RkpdStatsLog 253 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_ETHERNET_VPN; 254 } 255 return RkpdStatsLog 256 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_UNKNOWN; 257 } 258 259 if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { 260 return RkpdStatsLog 261 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_CELLULAR; 262 } 263 if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { 264 return RkpdStatsLog 265 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_WIFI; 266 } 267 if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) { 268 return RkpdStatsLog 269 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_BLUETOOTH; 270 } 271 if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { 272 return RkpdStatsLog 273 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_ETHERNET; 274 } 275 if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE)) { 276 return RkpdStatsLog 277 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_WIFI_AWARE; 278 } 279 if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_LOWPAN)) { 280 return RkpdStatsLog 281 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_LOWPAN; 282 } 283 284 return RkpdStatsLog 285 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__TRANSPORT_TYPE__TT_UNKNOWN; 286 } 287 getUpTimeBucket()288 private int getUpTimeBucket() { 289 final long uptimeMillis = SystemClock.uptimeMillis(); 290 if (uptimeMillis < Duration.ofMinutes(5).toMillis()) { 291 return RkpdStatsLog 292 .REMOTE_KEY_PROVISIONING_ATTEMPT__UPTIME__LESS_THAN_5_MINUTES; 293 } else if (uptimeMillis < Duration.ofMinutes(60).toMillis()) { 294 return RkpdStatsLog 295 .REMOTE_KEY_PROVISIONING_ATTEMPT__UPTIME__BETWEEN_5_AND_60_MINUTES; 296 } else { 297 return RkpdStatsLog 298 .REMOTE_KEY_PROVISIONING_ATTEMPT__UPTIME__MORE_THAN_60_MINUTES; 299 } 300 } 301 getIntStatus()302 private int getIntStatus() { 303 switch (mStatus) { 304 // A whole bunch of generated types here just don't fit in our line length limit. 305 // CHECKSTYLE:OFF Generated code 306 case UNKNOWN: 307 return RkpdStatsLog 308 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__REMOTE_KEY_PROVISIONING_STATUS_UNKNOWN; 309 case KEYS_SUCCESSFULLY_PROVISIONED: 310 return RkpdStatsLog 311 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__KEYS_SUCCESSFULLY_PROVISIONED; 312 case NO_PROVISIONING_NEEDED: 313 return RkpdStatsLog 314 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__NO_PROVISIONING_NEEDED; 315 case PROVISIONING_DISABLED: 316 return RkpdStatsLog 317 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__PROVISIONING_DISABLED; 318 case INTERNAL_ERROR: 319 return RkpdStatsLog 320 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__INTERNAL_ERROR; 321 case NO_NETWORK_CONNECTIVITY: 322 return RkpdStatsLog 323 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__NO_NETWORK_CONNECTIVITY; 324 case OUT_OF_ERROR_BUDGET: 325 return RkpdStatsLog 326 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__OUT_OF_ERROR_BUDGET; 327 case INTERRUPTED: 328 return RkpdStatsLog 329 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__INTERRUPTED; 330 case GENERATE_KEYPAIR_FAILED: 331 return RkpdStatsLog 332 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__GENERATE_KEYPAIR_FAILED; 333 case GENERATE_CSR_FAILED: 334 return RkpdStatsLog 335 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__GENERATE_CSR_FAILED; 336 case GET_POOL_STATUS_FAILED: 337 return RkpdStatsLog 338 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__GET_POOL_STATUS_FAILED; 339 case INSERT_CHAIN_INTO_POOL_FAILED: 340 return RkpdStatsLog 341 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__INSERT_CHAIN_INTO_POOL_FAILED; 342 case FETCH_GEEK_TIMED_OUT: 343 return RkpdStatsLog 344 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__FETCH_GEEK_TIMED_OUT; 345 case FETCH_GEEK_IO_EXCEPTION: 346 return RkpdStatsLog 347 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__FETCH_GEEK_IO_EXCEPTION; 348 case FETCH_GEEK_HTTP_ERROR: 349 return RkpdStatsLog 350 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__FETCH_GEEK_HTTP_ERROR; 351 case SIGN_CERTS_TIMED_OUT: 352 return RkpdStatsLog 353 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__SIGN_CERTS_TIMED_OUT; 354 case SIGN_CERTS_IO_EXCEPTION: 355 return RkpdStatsLog 356 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__SIGN_CERTS_IO_EXCEPTION; 357 case SIGN_CERTS_HTTP_ERROR: 358 return RkpdStatsLog 359 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__SIGN_CERTS_HTTP_ERROR; 360 case SIGN_CERTS_DEVICE_NOT_REGISTERED: 361 return RkpdStatsLog 362 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__SIGN_CERTS_DEVICE_NOT_REGISTERED; 363 } 364 return RkpdStatsLog 365 .REMOTE_KEY_PROVISIONING_NETWORK_INFO__STATUS__REMOTE_KEY_PROVISIONING_STATUS_UNKNOWN; 366 // CHECKSTYLE:ON Generated code 367 } 368 getIntEnablement()369 private int getIntEnablement() { 370 switch (mEnablement) { 371 case UNKNOWN: 372 return RkpdStatsLog 373 .REMOTE_KEY_PROVISIONING_ATTEMPT__ENABLEMENT__ENABLEMENT_UNKNOWN; 374 case ENABLED_WITH_FALLBACK: 375 return RkpdStatsLog 376 .REMOTE_KEY_PROVISIONING_ATTEMPT__ENABLEMENT__ENABLED_WITH_FALLBACK; 377 case ENABLED_RKP_ONLY: 378 return RkpdStatsLog 379 .REMOTE_KEY_PROVISIONING_ATTEMPT__ENABLEMENT__ENABLED_RKP_ONLY; 380 case DISABLED: 381 return RkpdStatsLog 382 .REMOTE_KEY_PROVISIONING_ATTEMPT__ENABLEMENT__DISABLED; 383 } 384 return RkpdStatsLog 385 .REMOTE_KEY_PROVISIONING_ATTEMPT__ENABLEMENT__ENABLEMENT_UNKNOWN; 386 } 387 } 388