1 /* 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 17 package com.android.server.power; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.os.Environment; 22 import android.os.IBinder; 23 import android.os.ParcelFileDescriptor; 24 import android.os.RemoteException; 25 import android.os.ServiceManager; 26 import android.provider.Settings; 27 import android.util.Slog; 28 29 import com.android.internal.annotations.GuardedBy; 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.internal.util.ArrayUtils; 32 33 import java.io.File; 34 import java.io.FileNotFoundException; 35 import java.io.FileWriter; 36 import java.io.IOException; 37 38 /** 39 * Provides utils to dump/wipe pre-reboot information. 40 */ 41 final 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 */ log(Context context)54 static void log(Context context) { 55 log(context, getDumpDir()); 56 } 57 58 @VisibleForTesting log(Context context, @NonNull File dumpDir)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 dump(@onNull File dumpDir)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 wipe(@onNull File dumpDir)81 private static void wipe(@NonNull File dumpDir) { 82 synchronized (sLock) { 83 for (File file : dumpDir.listFiles()) { 84 file.delete(); 85 } 86 } 87 } 88 getDumpDir()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") dumpLogsLocked(@onNull File dumpDir, @NonNull String buffer)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") dumpServiceLocked(@onNull File dumpDir, @NonNull String serviceName)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 } 134