/*
|
* Copyright (C) 2018 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;
|
|
import android.content.Context;
|
import android.os.Binder;
|
import android.service.runtime.DebugEntryProto;
|
import android.service.runtime.RuntimeServiceInfoProto;
|
import android.util.Slog;
|
import android.util.proto.ProtoOutputStream;
|
|
import libcore.timezone.TimeZoneDataFiles;
|
import libcore.util.CoreLibraryDebug;
|
import libcore.util.DebugInfo;
|
|
import com.android.internal.util.DumpUtils;
|
import com.android.timezone.distro.DistroException;
|
import com.android.timezone.distro.DistroVersion;
|
import com.android.timezone.distro.FileUtils;
|
import com.android.timezone.distro.TimeZoneDistro;
|
|
import java.io.File;
|
import java.io.FileDescriptor;
|
import java.io.IOException;
|
import java.io.PrintWriter;
|
|
/**
|
* This service exists only as a "dumpsys" target which reports information about the status of the
|
* runtime and related libraries.
|
*/
|
public class RuntimeService extends Binder {
|
|
private static final String TAG = "RuntimeService";
|
|
private final Context mContext;
|
|
public RuntimeService(Context context) {
|
mContext = context;
|
}
|
|
@Override
|
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
|
return;
|
}
|
|
boolean protoFormat = hasOption(args, "--proto");
|
ProtoOutputStream proto = null;
|
|
DebugInfo coreLibraryDebugInfo = CoreLibraryDebug.getDebugInfo();
|
addTimeZoneApkDebugInfo(coreLibraryDebugInfo);
|
|
if (protoFormat) {
|
proto = new ProtoOutputStream(fd);
|
reportTimeZoneInfoProto(coreLibraryDebugInfo, proto);
|
} else {
|
reportTimeZoneInfo(coreLibraryDebugInfo, pw);
|
}
|
|
if (protoFormat) {
|
proto.flush();
|
}
|
}
|
|
/** Returns {@code true} if {@code args} contains {@code arg}. */
|
private static boolean hasOption(String[] args, String arg) {
|
for (String opt : args) {
|
if (arg.equals(opt)) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
/**
|
* Add information to {@link DebugInfo} about the time zone data supplied by the
|
* "Time zone updates via APK" feature.
|
*/
|
private static void addTimeZoneApkDebugInfo(DebugInfo coreLibraryDebugInfo) {
|
// Add /data tz data set using the DistroVersion class (which libcore cannot use).
|
// This update mechanism will be removed after the time zone APEX is launched so this
|
// untidiness will disappear with it.
|
String debugKeyPrefix = "core_library.timezone.source.data_";
|
String versionFileName = TimeZoneDataFiles.getDataTimeZoneFile(
|
TimeZoneDistro.DISTRO_VERSION_FILE_NAME);
|
addDistroVersionDebugInfo(versionFileName, debugKeyPrefix, coreLibraryDebugInfo);
|
}
|
|
/**
|
* Prints {@code coreLibraryDebugInfo} to {@code pw}.
|
*
|
* <p>If you change this method, make sure to modify
|
* {@link #reportTimeZoneInfoProto(DebugInfo, ProtoOutputStream)} as well.
|
*/
|
private static void reportTimeZoneInfo(DebugInfo coreLibraryDebugInfo,
|
PrintWriter pw) {
|
pw.println("Core Library Debug Info: ");
|
for (DebugInfo.DebugEntry debugEntry : coreLibraryDebugInfo.getDebugEntries()) {
|
pw.print(debugEntry.getKey());
|
pw.print(": \"");
|
pw.print(debugEntry.getStringValue());
|
pw.println("\"");
|
}
|
}
|
|
/**
|
* Adds {@code coreLibraryDebugInfo} to {@code protoStream}.
|
*
|
* <p>If you change this method, make sure to modify
|
* {@link #reportTimeZoneInfo(DebugInfo, PrintWriter)}.
|
*/
|
private static void reportTimeZoneInfoProto(
|
DebugInfo coreLibraryDebugInfo, ProtoOutputStream protoStream) {
|
for (DebugInfo.DebugEntry debugEntry : coreLibraryDebugInfo.getDebugEntries()) {
|
long entryToken = protoStream.start(RuntimeServiceInfoProto.DEBUG_ENTRY);
|
protoStream.write(DebugEntryProto.KEY, debugEntry.getKey());
|
protoStream.write(DebugEntryProto.STRING_VALUE, debugEntry.getStringValue());
|
protoStream.end(entryToken);
|
}
|
}
|
|
/**
|
* Adds version information to {@code debugInfo} from the distro_version file that may exist
|
* at {@code distroVersionFileName}. If the file does not exist or cannot be read this is
|
* reported as debug information too.
|
*/
|
private static void addDistroVersionDebugInfo(String distroVersionFileName,
|
String debugKeyPrefix, DebugInfo debugInfo) {
|
File file = new File(distroVersionFileName);
|
String statusKey = debugKeyPrefix + "status";
|
if (file.exists()) {
|
try {
|
byte[] versionBytes =
|
FileUtils.readBytes(file, DistroVersion.DISTRO_VERSION_FILE_LENGTH);
|
DistroVersion distroVersion = DistroVersion.fromBytes(versionBytes);
|
String formatVersionString = distroVersion.formatMajorVersion + "."
|
+ distroVersion.formatMinorVersion;
|
debugInfo.addStringEntry(statusKey, "OK")
|
.addStringEntry(debugKeyPrefix + "formatVersion", formatVersionString)
|
.addStringEntry(debugKeyPrefix + "rulesVersion",
|
distroVersion.rulesVersion)
|
.addStringEntry(debugKeyPrefix + "revision",
|
distroVersion.revision);
|
} catch (IOException | DistroException e) {
|
debugInfo.addStringEntry(statusKey, "ERROR");
|
debugInfo.addStringEntry(debugKeyPrefix + "exception_class",
|
e.getClass().getName());
|
debugInfo.addStringEntry(debugKeyPrefix + "exception_msg", e.getMessage());
|
logMessage("Error reading " + file, e);
|
}
|
} else {
|
debugInfo.addStringEntry(statusKey, "NOT_FOUND");
|
}
|
}
|
|
private static void logMessage(String msg, Throwable t) {
|
Slog.v(TAG, msg, t);
|
}
|
}
|