blob: cda00b404c0b6f94a135fafd6fe218b7fc2d43f5 [file] [log] [blame]
Jerry Chang1f823472019-12-13 19:07:52 +08001/*
2 * Copyright (C) 2020 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.power;
18
19import android.annotation.NonNull;
20import android.content.Context;
21import android.os.Environment;
22import android.os.IBinder;
23import android.os.ParcelFileDescriptor;
24import android.os.RemoteException;
25import android.os.ServiceManager;
26import android.provider.Settings;
27import android.util.Slog;
28
29import com.android.internal.annotations.GuardedBy;
30import com.android.internal.annotations.VisibleForTesting;
31import com.android.internal.util.ArrayUtils;
32
33import java.io.File;
34import java.io.FileNotFoundException;
35import java.io.FileWriter;
36import java.io.IOException;
37
38/**
39 * Provides utils to dump/wipe pre-reboot information.
40 */
41final class PreRebootLogger {
42 private static final String TAG = "PreRebootLogger";
43 private static final String PREREBOOT_DIR = "prereboot";
44
45 private static final String[] BUFFERS_TO_DUMP = {"system"};
46 private static final String[] SERVICES_TO_DUMP = {Context.ROLLBACK_SERVICE, "package"};
47
48 private static final Object sLock = new Object();
49
50 /**
51 * Process pre-reboot information. Dump pre-reboot information to {@link #PREREBOOT_DIR} if
52 * enabled {@link Settings.Global#ADB_ENABLED}; wipe dumped information otherwise.
53 */
54 static void log(Context context) {
55 log(context, getDumpDir());
56 }
57
58 @VisibleForTesting
59 static void log(Context context, @NonNull File dumpDir) {
60 if (Settings.Global.getInt(
61 context.getContentResolver(), Settings.Global.ADB_ENABLED, 0) == 1) {
62 Slog.d(TAG, "Dumping pre-reboot information...");
63 dump(dumpDir);
64 } else {
65 Slog.d(TAG, "Wiping pre-reboot information...");
66 wipe(dumpDir);
67 }
68 }
69
70 private static void dump(@NonNull File dumpDir) {
71 synchronized (sLock) {
72 for (String buffer : BUFFERS_TO_DUMP) {
73 dumpLogsLocked(dumpDir, buffer);
74 }
75 for (String service : SERVICES_TO_DUMP) {
76 dumpServiceLocked(dumpDir, service);
77 }
78 }
79 }
80
81 private static void wipe(@NonNull File dumpDir) {
82 synchronized (sLock) {
83 for (File file : dumpDir.listFiles()) {
84 file.delete();
85 }
86 }
87 }
88
89 private static File getDumpDir() {
90 final File dumpDir = new File(Environment.getDataMiscDirectory(), PREREBOOT_DIR);
91 if (!dumpDir.exists() || !dumpDir.isDirectory()) {
92 throw new UnsupportedOperationException("Pre-reboot dump directory not found");
93 }
94 return dumpDir;
95 }
96
97 @GuardedBy("sLock")
98 private static void dumpLogsLocked(@NonNull File dumpDir, @NonNull String buffer) {
99 try {
100 final File dumpFile = new File(dumpDir, buffer);
101 if (dumpFile.createNewFile()) {
102 dumpFile.setWritable(true /* writable */, true /* ownerOnly */);
103 } else {
104 // Wipes dumped information in existing file before recording new information.
105 new FileWriter(dumpFile, false).flush();
106 }
107
108 final String[] cmdline =
109 {"logcat", "-d", "-b", buffer, "-f", dumpFile.getAbsolutePath()};
110 Runtime.getRuntime().exec(cmdline).waitFor();
111 } catch (IOException | InterruptedException e) {
112 Slog.d(TAG, "Dump system log buffer before reboot fail", e);
113 }
114 }
115
116 @GuardedBy("sLock")
117 private static void dumpServiceLocked(@NonNull File dumpDir, @NonNull String serviceName) {
118 final IBinder binder = ServiceManager.checkService(serviceName);
119 if (binder == null) {
120 return;
121 }
122
123 try {
124 final File dumpFile = new File(dumpDir, serviceName);
125 final ParcelFileDescriptor fd = ParcelFileDescriptor.open(dumpFile,
126 ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE
127 | ParcelFileDescriptor.MODE_WRITE_ONLY);
128 binder.dump(fd.getFileDescriptor(), ArrayUtils.emptyArray(String.class));
129 } catch (FileNotFoundException | RemoteException e) {
130 Slog.d(TAG, String.format("Dump %s service before reboot fail", serviceName), e);
131 }
132 }
133}