| /* |
| * Copyright 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.pm.dex; |
| |
| import android.util.Slog; |
| |
| import com.android.internal.art.ArtStatsLog; |
| import com.android.internal.os.BackgroundThread; |
| |
| import libcore.io.IoUtils; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| |
| /** |
| * This class is responsible for reading metrics files generated by odsign and sending them to |
| * statsd. odsign can't send the stats directly to statsd, because statsd can't run until after |
| * odsign has completed. The code here is intended to run once per boot, since odsign runs at boot |
| * time. |
| */ |
| public class OdsignStatsLogger { |
| private static final String TAG = "OdsignStatsLogger"; |
| |
| // These need to be kept in sync with system/security/ondevice-signing/StatsReporter.{h, cpp}. |
| private static final String METRICS_FILE = "/data/misc/odsign/metrics/odsign-metrics.txt"; |
| private static final String COMPOS_METRIC_NAME = "comp_os_artifacts_check_record"; |
| private static final String ODSIGN_METRIC_NAME = "odsign_record"; |
| |
| /** |
| * Arrange for stats to be uploaded in the background. |
| */ |
| public static void triggerStatsWrite() { |
| BackgroundThread.getExecutor().execute(OdsignStatsLogger::writeStats); |
| } |
| |
| private static void writeStats() { |
| try { |
| String lines = IoUtils.readFileAsString(METRICS_FILE); |
| |
| // Delete the file now so we don't upload it more than once, and don't keep trying |
| // to re-parse it if there is a problem. |
| if (!new File(METRICS_FILE).delete()) { |
| Slog.w(TAG, "Failed to delete metrics file"); |
| } |
| |
| // The format is simple - each line is a series of space separated tokens. The first is |
| // the metric name and subsequent ones are the metric values. The logic here must be |
| // kept in sync with system/security/ondevice-signing/StatsReporter.cpp. |
| |
| for (String line : lines.split("\n")) { |
| String[] metrics = line.split(" "); |
| |
| if (line.isEmpty() || metrics.length < 1) { |
| Slog.w(TAG, "Empty metrics line"); |
| continue; |
| } |
| |
| switch (metrics[0]) { |
| case COMPOS_METRIC_NAME: { |
| if (metrics.length != 4) { |
| Slog.w(TAG, "Malformed CompOS metrics line '" + line + "'"); |
| continue; |
| } |
| |
| boolean currentArtifactsOk = metrics[1].equals("1"); |
| boolean compOsPendingArtifactsExists = metrics[2].equals("1"); |
| boolean useCompOsGeneratedArtifacts = metrics[3].equals("1"); |
| |
| ArtStatsLog.write(ArtStatsLog.EARLY_BOOT_COMP_OS_ARTIFACTS_CHECK_REPORTED, |
| currentArtifactsOk, compOsPendingArtifactsExists, |
| useCompOsGeneratedArtifacts); |
| break; |
| } |
| case ODSIGN_METRIC_NAME: { |
| if (metrics.length != 2) { |
| Slog.w(TAG, "Malformed odsign metrics line '" + line + "'"); |
| continue; |
| } |
| |
| try { |
| int status = Integer.parseInt(metrics[1]); |
| ArtStatsLog.write(ArtStatsLog.ODSIGN_REPORTED, status); |
| } catch (NumberFormatException e) { |
| Slog.w(TAG, "Malformed odsign metrics line '" + line + "'"); |
| } |
| |
| break; |
| } |
| default: |
| Slog.w(TAG, "Malformed metrics line '" + line + "'"); |
| } |
| } |
| } catch (FileNotFoundException e) { |
| // This is normal and probably means no new metrics have been generated. |
| } catch (IOException e) { |
| Slog.w(TAG, "Reading metrics file failed", e); |
| } |
| } |
| } |