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, "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, "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>This call should only be made on devices supporting userspace reboot.
92      */
shouldLogUserspaceRebootEvent()93     public static boolean shouldLogUserspaceRebootEvent() {
94         if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
95             Slog.wtf(TAG, "Userspace reboot is not supported.");
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, "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