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