blob: bb39ccc52af22a7adaf1e6a984266c6b803503c5 [file] [log] [blame]
Neil Fullerab0b81e2018-11-20 19:37:22 +00001/*
2 * Copyright (C) 2018 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
17package com.android.server;
18
19import android.content.Context;
20import android.os.Binder;
21import android.service.runtime.DebugEntryProto;
22import android.service.runtime.RuntimeServiceInfoProto;
23import android.util.Slog;
24import android.util.proto.ProtoOutputStream;
25
26import libcore.timezone.TimeZoneDataFiles;
27import libcore.util.CoreLibraryDebug;
28import libcore.util.DebugInfo;
29
30import com.android.internal.util.DumpUtils;
31import com.android.timezone.distro.DistroException;
32import com.android.timezone.distro.DistroVersion;
33import com.android.timezone.distro.FileUtils;
34import com.android.timezone.distro.TimeZoneDistro;
35
36import java.io.File;
37import java.io.FileDescriptor;
38import java.io.IOException;
39import java.io.PrintWriter;
40
41/**
42 * This service exists only as a "dumpsys" target which reports information about the status of the
43 * runtime and related libraries.
44 */
45public class RuntimeService extends Binder {
46
47 private static final String TAG = "RuntimeService";
48
49 private final Context mContext;
50
51 public RuntimeService(Context context) {
52 mContext = context;
53 }
54
55 @Override
56 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
57 if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
58 return;
59 }
60
61 boolean protoFormat = hasOption(args, "--proto");
62 ProtoOutputStream proto = null;
63
64 DebugInfo coreLibraryDebugInfo = CoreLibraryDebug.getDebugInfo();
65 addTimeZoneApkDebugInfo(coreLibraryDebugInfo);
66
67 if (protoFormat) {
68 proto = new ProtoOutputStream(fd);
69 reportTimeZoneInfoProto(coreLibraryDebugInfo, proto);
70 } else {
71 reportTimeZoneInfo(coreLibraryDebugInfo, pw);
72 }
73
74 if (protoFormat) {
75 proto.flush();
76 }
77 }
78
79 /** Returns {@code true} if {@code args} contains {@code arg}. */
80 private static boolean hasOption(String[] args, String arg) {
81 for (String opt : args) {
82 if (arg.equals(opt)) {
83 return true;
84 }
85 }
86 return false;
87 }
88
89 /**
90 * Add information to {@link DebugInfo} about the time zone data supplied by the
91 * "Time zone updates via APK" feature.
92 */
93 private static void addTimeZoneApkDebugInfo(DebugInfo coreLibraryDebugInfo) {
94 // Add /data tz data set using the DistroVersion class (which libcore cannot use).
95 // This update mechanism will be removed after the time zone APEX is launched so this
96 // untidiness will disappear with it.
Neil Fuller352afd12018-12-04 21:17:29 +000097 String debugKeyPrefix = "core_library.timezone.source.data_";
Neil Fullerab0b81e2018-11-20 19:37:22 +000098 String versionFileName = TimeZoneDataFiles.getDataTimeZoneFile(
99 TimeZoneDistro.DISTRO_VERSION_FILE_NAME);
100 addDistroVersionDebugInfo(versionFileName, debugKeyPrefix, coreLibraryDebugInfo);
101 }
102
103 /**
104 * Prints {@code coreLibraryDebugInfo} to {@code pw}.
105 *
106 * <p>If you change this method, make sure to modify
107 * {@link #reportTimeZoneInfoProto(DebugInfo, ProtoOutputStream)} as well.
108 */
109 private static void reportTimeZoneInfo(DebugInfo coreLibraryDebugInfo,
110 PrintWriter pw) {
111 pw.println("Core Library Debug Info: ");
112 for (DebugInfo.DebugEntry debugEntry : coreLibraryDebugInfo.getDebugEntries()) {
113 pw.print(debugEntry.getKey());
114 pw.print(": \"");
115 pw.print(debugEntry.getStringValue());
116 pw.println("\"");
117 }
118 }
119
120 /**
121 * Adds {@code coreLibraryDebugInfo} to {@code protoStream}.
122 *
123 * <p>If you change this method, make sure to modify
124 * {@link #reportTimeZoneInfo(DebugInfo, PrintWriter)}.
125 */
126 private static void reportTimeZoneInfoProto(
127 DebugInfo coreLibraryDebugInfo, ProtoOutputStream protoStream) {
128 for (DebugInfo.DebugEntry debugEntry : coreLibraryDebugInfo.getDebugEntries()) {
129 long entryToken = protoStream.start(RuntimeServiceInfoProto.DEBUG_ENTRY);
130 protoStream.write(DebugEntryProto.KEY, debugEntry.getKey());
131 protoStream.write(DebugEntryProto.STRING_VALUE, debugEntry.getStringValue());
132 protoStream.end(entryToken);
133 }
134 }
135
136 /**
137 * Adds version information to {@code debugInfo} from the distro_version file that may exist
138 * at {@code distroVersionFileName}. If the file does not exist or cannot be read this is
139 * reported as debug information too.
140 */
141 private static void addDistroVersionDebugInfo(String distroVersionFileName,
142 String debugKeyPrefix, DebugInfo debugInfo) {
143 File file = new File(distroVersionFileName);
144 String statusKey = debugKeyPrefix + "status";
145 if (file.exists()) {
146 try {
147 byte[] versionBytes =
148 FileUtils.readBytes(file, DistroVersion.DISTRO_VERSION_FILE_LENGTH);
149 DistroVersion distroVersion = DistroVersion.fromBytes(versionBytes);
150 String formatVersionString = distroVersion.formatMajorVersion + "."
151 + distroVersion.formatMinorVersion;
152 debugInfo.addStringEntry(statusKey, "OK")
153 .addStringEntry(debugKeyPrefix + "formatVersion", formatVersionString)
154 .addStringEntry(debugKeyPrefix + "rulesVersion",
155 distroVersion.rulesVersion)
156 .addStringEntry(debugKeyPrefix + "revision",
157 distroVersion.revision);
158 } catch (IOException | DistroException e) {
159 debugInfo.addStringEntry(statusKey, "ERROR");
160 debugInfo.addStringEntry(debugKeyPrefix + "exception_class",
161 e.getClass().getName());
162 debugInfo.addStringEntry(debugKeyPrefix + "exception_msg", e.getMessage());
163 logMessage("Error reading " + file, e);
164 }
165 } else {
166 debugInfo.addStringEntry(statusKey, "NOT_FOUND");
167 }
168 }
169
170 private static void logMessage(String msg, Throwable t) {
171 Slog.v(TAG, msg, t);
172 }
173}