1 /* 2 * Copyright 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.server.pm.dex; 18 19 import android.util.Slog; 20 21 import com.android.internal.art.ArtStatsLog; 22 import com.android.internal.os.BackgroundThread; 23 24 import libcore.io.IoUtils; 25 26 import java.io.File; 27 import java.io.FileNotFoundException; 28 import java.io.IOException; 29 30 /** 31 * This class is responsible for reading metrics files generated by odsign and sending them to 32 * statsd. odsign can't send the stats directly to statsd, because statsd can't run until after 33 * odsign has completed. The code here is intended to run once per boot, since odsign runs at boot 34 * time. 35 */ 36 public class OdsignStatsLogger { 37 private static final String TAG = "OdsignStatsLogger"; 38 39 // These need to be kept in sync with system/security/ondevice-signing/StatsReporter.{h, cpp}. 40 private static final String METRICS_FILE = "/data/misc/odsign/metrics/odsign-metrics.txt"; 41 private static final String COMPOS_METRIC_NAME = "comp_os_artifacts_check_record"; 42 private static final String ODSIGN_METRIC_NAME = "odsign_record"; 43 44 /** 45 * Arrange for stats to be uploaded in the background. 46 */ triggerStatsWrite()47 public static void triggerStatsWrite() { 48 BackgroundThread.getExecutor().execute(OdsignStatsLogger::writeStats); 49 } 50 writeStats()51 private static void writeStats() { 52 try { 53 String lines = IoUtils.readFileAsString(METRICS_FILE); 54 55 // Delete the file now so we don't upload it more than once, and don't keep trying 56 // to re-parse it if there is a problem. 57 if (!new File(METRICS_FILE).delete()) { 58 Slog.w(TAG, "Failed to delete metrics file"); 59 } 60 61 // The format is simple - each line is a series of space separated tokens. The first is 62 // the metric name and subsequent ones are the metric values. The logic here must be 63 // kept in sync with system/security/ondevice-signing/StatsReporter.cpp. 64 65 for (String line : lines.split("\n")) { 66 String[] metrics = line.split(" "); 67 68 if (line.isEmpty() || metrics.length < 1) { 69 Slog.w(TAG, "Empty metrics line"); 70 continue; 71 } 72 73 switch (metrics[0]) { 74 case COMPOS_METRIC_NAME: { 75 if (metrics.length != 4) { 76 Slog.w(TAG, "Malformed CompOS metrics line '" + line + "'"); 77 continue; 78 } 79 80 boolean currentArtifactsOk = metrics[1].equals("1"); 81 boolean compOsPendingArtifactsExists = metrics[2].equals("1"); 82 boolean useCompOsGeneratedArtifacts = metrics[3].equals("1"); 83 84 ArtStatsLog.write(ArtStatsLog.EARLY_BOOT_COMP_OS_ARTIFACTS_CHECK_REPORTED, 85 currentArtifactsOk, compOsPendingArtifactsExists, 86 useCompOsGeneratedArtifacts); 87 break; 88 } 89 case ODSIGN_METRIC_NAME: { 90 if (metrics.length != 2) { 91 Slog.w(TAG, "Malformed odsign metrics line '" + line + "'"); 92 continue; 93 } 94 95 try { 96 int status = Integer.parseInt(metrics[1]); 97 ArtStatsLog.write(ArtStatsLog.ODSIGN_REPORTED, status); 98 } catch (NumberFormatException e) { 99 Slog.w(TAG, "Malformed odsign metrics line '" + line + "'"); 100 } 101 102 break; 103 } 104 default: 105 Slog.w(TAG, "Malformed metrics line '" + line + "'"); 106 } 107 } 108 } catch (FileNotFoundException e) { 109 // This is normal and probably means no new metrics have been generated. 110 } catch (IOException e) { 111 Slog.w(TAG, "Reading metrics file failed", e); 112 } 113 } 114 } 115