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; 18 19 import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED; 20 import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT; 21 import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED; 22 import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN; 23 import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS; 24 import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__LOCKED; 25 import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__UNLOCKED; 26 27 import android.os.PowerManager; 28 import android.os.SystemClock; 29 import android.os.SystemProperties; 30 import android.text.TextUtils; 31 import android.util.Slog; 32 33 import com.android.internal.util.FrameworkStatsLog; 34 35 import java.util.concurrent.Executor; 36 37 /** 38 * Utility class to help abstract logging {@code UserspaceRebootReported} atom. 39 */ 40 public final class UserspaceRebootLogger { 41 42 private static final String TAG = "UserspaceRebootLogger"; 43 44 private static final String USERSPACE_REBOOT_SHOULD_LOG_PROPERTY = 45 "persist.sys.userspace_reboot.log.should_log"; 46 private static final String USERSPACE_REBOOT_LAST_STARTED_PROPERTY = 47 "sys.userspace_reboot.log.last_started"; 48 private static final String USERSPACE_REBOOT_LAST_FINISHED_PROPERTY = 49 "sys.userspace_reboot.log.last_finished"; 50 private static final String LAST_BOOT_REASON_PROPERTY = "sys.boot.reason.last"; 51 UserspaceRebootLogger()52 private UserspaceRebootLogger() {} 53 54 /** 55 * Modifies internal state to note that {@code UserspaceRebootReported} atom needs to be 56 * logged on the next successful boot. 57 * 58 * <p>This call should only be made on devices supporting userspace reboot. 59 */ noteUserspaceRebootWasRequested()60 public static void noteUserspaceRebootWasRequested() { 61 if (!PowerManager.isRebootingUserspaceSupportedImpl()) { 62 Slog.wtf(TAG, "noteUserspaceRebootWasRequested: Userspace reboot is not supported."); 63 return; 64 } 65 66 SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, "1"); 67 SystemProperties.set(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, 68 String.valueOf(SystemClock.elapsedRealtime())); 69 } 70 71 /** 72 * Updates internal state on boot after successful userspace reboot. 73 * 74 * <p>Should be called right before framework sets {@code sys.boot_completed} property. 75 * 76 * <p>This call should only be made on devices supporting userspace reboot. 77 */ noteUserspaceRebootSuccess()78 public static void noteUserspaceRebootSuccess() { 79 if (!PowerManager.isRebootingUserspaceSupportedImpl()) { 80 Slog.wtf(TAG, "noteUserspaceRebootSuccess: Userspace reboot is not supported."); 81 return; 82 } 83 84 SystemProperties.set(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY, 85 String.valueOf(SystemClock.elapsedRealtime())); 86 } 87 88 /** 89 * Returns {@code true} if {@code UserspaceRebootReported} atom should be logged. 90 * 91 * <p>On devices that do not support userspace reboot this method will always return {@code 92 * false}. 93 */ shouldLogUserspaceRebootEvent()94 public static boolean shouldLogUserspaceRebootEvent() { 95 if (!PowerManager.isRebootingUserspaceSupportedImpl()) { 96 return false; 97 } 98 99 return SystemProperties.getBoolean(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, false); 100 } 101 102 /** 103 * Asynchronously logs {@code UserspaceRebootReported} on the given {@code executor}. 104 * 105 * <p>Should be called in the end of {@link 106 * com.android.server.am.ActivityManagerService#finishBooting()} method, after framework have 107 * tried to proactivelly unlock storage of the primary user. 108 * 109 * <p>This call should only be made on devices supporting userspace reboot. 110 */ logEventAsync(boolean userUnlocked, Executor executor)111 public static void logEventAsync(boolean userUnlocked, Executor executor) { 112 if (!PowerManager.isRebootingUserspaceSupportedImpl()) { 113 Slog.wtf(TAG, "logEventAsync: Userspace reboot is not supported."); 114 return; 115 } 116 117 final int outcome = computeOutcome(); 118 final long durationMillis; 119 if (outcome == USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS) { 120 durationMillis = SystemProperties.getLong(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY, 0) 121 - SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, 0); 122 } else { 123 durationMillis = 0; 124 } 125 final int encryptionState = 126 userUnlocked 127 ? USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__UNLOCKED 128 : USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__LOCKED; 129 executor.execute( 130 () -> { 131 Slog.i(TAG, "Logging UserspaceRebootReported atom: { outcome: " + outcome 132 + " durationMillis: " + durationMillis + " encryptionState: " 133 + encryptionState + " }"); 134 FrameworkStatsLog.write(FrameworkStatsLog.USERSPACE_REBOOT_REPORTED, outcome, 135 durationMillis, encryptionState); 136 SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, ""); 137 }); 138 } 139 computeOutcome()140 private static int computeOutcome() { 141 if (SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, -1) != -1) { 142 return USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS; 143 } 144 String reason = TextUtils.emptyIfNull(SystemProperties.get(LAST_BOOT_REASON_PROPERTY, "")); 145 if (reason.startsWith("reboot,")) { 146 reason = reason.substring("reboot".length()); 147 } 148 if (reason.startsWith("userspace_failed,watchdog_fork")) { 149 return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED; 150 } 151 if (reason.startsWith("userspace_failed,shutdown_aborted")) { 152 return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED; 153 } 154 if (reason.startsWith("mount_userdata_failed")) { 155 return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT; 156 } 157 if (reason.startsWith("userspace_failed,init_user0")) { 158 return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT; 159 } 160 if (reason.startsWith("userspace_failed,enablefilecrypto")) { 161 return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT; 162 } 163 if (reason.startsWith("userspace_failed,watchdog_triggered")) { 164 return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED; 165 } 166 return USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN; 167 } 168 } 169