diff options
| -rw-r--r-- | Android.bp | 1 | ||||
| -rw-r--r-- | cmds/statsd/src/atoms.proto | 2 | ||||
| -rw-r--r-- | services/core/java/com/android/server/UserspaceRebootLogger.java | 136 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 23 | ||||
| -rw-r--r-- | services/core/java/com/android/server/power/PowerManagerService.java | 9 |
5 files changed, 168 insertions, 3 deletions
diff --git a/Android.bp b/Android.bp index ff3ad31306b5..5224348621b5 100644 --- a/Android.bp +++ b/Android.bp @@ -368,6 +368,7 @@ java_library { "devicepolicyprotosnano", "com.android.sysprop.apex", + "com.android.sysprop.init", "PlatformProperties", ], sdk_version: "core_platform", diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 1df81e91a9d2..0985f17cbaba 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -356,7 +356,7 @@ message Atom { BootTimeEventElapsedTime boot_time_event_elapsed_time_reported = 240; BootTimeEventUtcTime boot_time_event_utc_time_reported = 241; BootTimeEventErrorCode boot_time_event_error_code_reported = 242; - UserspaceRebootReported userspace_reboot_reported = 243; + UserspaceRebootReported userspace_reboot_reported = 243 [(log_from_module) = "framework"]; } // Pulled events will start at field 10000. diff --git a/services/core/java/com/android/server/UserspaceRebootLogger.java b/services/core/java/com/android/server/UserspaceRebootLogger.java new file mode 100644 index 000000000000..74f113f58c70 --- /dev/null +++ b/services/core/java/com/android/server/UserspaceRebootLogger.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 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 static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED; +import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT; +import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED; +import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN; +import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS; +import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__LOCKED; +import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__UNLOCKED; + +import android.os.SystemClock; +import android.os.SystemProperties; +import android.util.Slog; + +import com.android.internal.util.FrameworkStatsLog; + +import java.util.concurrent.Executor; + +/** + * Utility class to help abstract logging {@code UserspaceRebootReported} atom. + */ +public final class UserspaceRebootLogger { + + private static final String TAG = "UserspaceRebootLogger"; + + private static final String USERSPACE_REBOOT_SHOULD_LOG_PROPERTY = + "persist.sys.userspace_reboot.log.should_log"; + private static final String USERSPACE_REBOOT_LAST_STARTED_PROPERTY = + "sys.userspace_reboot.log.last_started"; + private static final String USERSPACE_REBOOT_LAST_FINISHED_PROPERTY = + "sys.userspace_reboot.log.last_finished"; + private static final String BOOT_REASON_PROPERTY = "sys.boot.reason"; + + private UserspaceRebootLogger() {} + + /** + * Modifies internal state to note that {@code UserspaceRebootReported} atom needs to be + * logged on the next successful boot. + */ + public static void noteUserspaceRebootWasRequested() { + SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, "1"); + SystemProperties.set(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, + String.valueOf(SystemClock.elapsedRealtime())); + } + + /** + * Updates internal state on boot after successful userspace reboot. + * + * <p>Should be called right before framework sets {@code sys.boot_completed} property. + */ + public static void noteUserspaceRebootSuccess() { + SystemProperties.set(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY, + String.valueOf(SystemClock.elapsedRealtime())); + } + + /** + * Returns {@code true} if {@code UserspaceRebootReported} atom should be logged. + */ + public static boolean shouldLogUserspaceRebootEvent() { + return SystemProperties.getBoolean(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, false); + } + + /** + * Asynchronously logs {@code UserspaceRebootReported} on the given {@code executor}. + * + * <p>Should be called in the end of {@link + * com.android.server.am.ActivityManagerService#finishBooting()} method, after framework have + * tried to proactivelly unlock storage of the primary user. + */ + public static void logEventAsync(boolean userUnlocked, Executor executor) { + final int outcome = computeOutcome(); + final long durationMillis; + if (outcome == USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS) { + durationMillis = SystemProperties.getLong(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY, 0) + - SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, 0); + } else { + durationMillis = 0; + } + final int encryptionState = + userUnlocked + ? USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__UNLOCKED + : USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__LOCKED; + executor.execute( + () -> { + Slog.i(TAG, "Logging UserspaceRebootReported atom: { outcome: " + outcome + + " durationMillis: " + durationMillis + " encryptionState: " + + encryptionState + " }"); + FrameworkStatsLog.write(FrameworkStatsLog.USERSPACE_REBOOT_REPORTED, outcome, + durationMillis, encryptionState); + SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, ""); + }); + } + + private static int computeOutcome() { + if (SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, -1) != -1) { + return USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS; + } + String reason = SystemProperties.get(BOOT_REASON_PROPERTY, ""); + if (reason.startsWith("reboot,")) { + reason = reason.substring("reboot".length()); + } + switch (reason) { + case "userspace_failed,watchdog_fork": + // Since fork happens before shutdown sequence, attribute it to + // USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED. + case "userspace_failed,shutdown_aborted": + return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED; + case "userspace_failed,init_user0_failed": + // init_user0 will fail if userdata wasn't remounted correctly, attribute to + // USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT. + case "mount_userdata_failed": + return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT; + case "userspace_failed,watchdog_triggered": + return + USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED; + default: + return USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN; + } + } +} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d7b716561dc4..5276666a8780 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -275,6 +275,7 @@ import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; import android.provider.Settings; import android.server.ServerProtoEnums; +import android.sysprop.InitProperties; import android.sysprop.VoldProperties; import android.text.TextUtils; import android.text.format.DateUtils; @@ -346,6 +347,7 @@ import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.SystemServiceManager; import com.android.server.ThreadPriorityBooster; +import com.android.server.UserspaceRebootLogger; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto; import com.android.server.appop.AppOpsService; @@ -2253,6 +2255,20 @@ public class ActivityManagerService extends IActivityManager.Stub } } + private void maybeLogUserspaceRebootEvent() { + if (!UserspaceRebootLogger.shouldLogUserspaceRebootEvent()) { + return; + } + final int userId = mUserController.getCurrentUserId(); + if (userId != UserHandle.USER_SYSTEM) { + // Only log for user0. + return; + } + // TODO(b/148767783): should we check all profiles under user0? + UserspaceRebootLogger.logEventAsync(StorageManager.isUserKeyUnlocked(userId), + BackgroundThread.getExecutor()); + } + /** * Encapsulates global settings related to hidden API enforcement behaviour, including tracking * the latest value via a content observer. @@ -5306,6 +5322,12 @@ public class ActivityManagerService extends IActivityManager.Stub // Start looking for apps that are abusing wake locks. Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG); mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL); + // Check if we are performing userspace reboot before setting sys.boot_completed to + // avoid race with init reseting sys.init.userspace_reboot.in_progress once sys + // .boot_completed is 1. + if (InitProperties.userspace_reboot_in_progress().orElse(false)) { + UserspaceRebootLogger.noteUserspaceRebootSuccess(); + } // Tell anyone interested that we are done booting! SystemProperties.set("sys.boot_completed", "1"); @@ -5326,6 +5348,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } }); + maybeLogUserspaceRebootEvent(); mUserController.scheduleStartProfiles(); } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 3a7604aa7d69..26a623f9a1fc 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -23,6 +23,7 @@ import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.SynchronousUserSwitchObserver; @@ -91,6 +92,7 @@ import com.android.server.RescueParty; import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.UiThread; +import com.android.server.UserspaceRebootLogger; import com.android.server.Watchdog; import com.android.server.am.BatteryStatsService; import com.android.server.lights.LightsManager; @@ -2834,7 +2836,10 @@ public final class PowerManagerService extends SystemService } private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm, - final String reason, boolean wait) { + @Nullable final String reason, boolean wait) { + if (PowerManager.REBOOT_USERSPACE.equals(reason)) { + UserspaceRebootLogger.noteUserspaceRebootWasRequested(); + } if (mHandler == null || !mSystemReady) { if (RescueParty.isAttemptingFactoryReset()) { // If we're stuck in a really low-level reboot loop, and a @@ -4649,7 +4654,7 @@ public final class PowerManagerService extends SystemService * @param wait If true, this call waits for the reboot to complete and does not return. */ @Override // Binder call - public void reboot(boolean confirm, String reason, boolean wait) { + public void reboot(boolean confirm, @Nullable String reason, boolean wait) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); if (PowerManager.REBOOT_RECOVERY.equals(reason) || PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) { |