Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 779d6bee authored by Nikita Ioffe's avatar Nikita Ioffe
Browse files

Log UserspaceRebootReported atom from system_server

UserspaceRebootLogger class is introduced to encapsulate (tiny) state
machine for logging of userspace reboot.

So far state is stored in a persistent property, but that can be changed
in the future.

Unit tests will be added in a follow-up CL.

Test: adb shell svc power reboot userspace
Bug: 148767783
Change-Id: Ib9e2c6cef7094ccac7862be249d619212f0013fd
(cherry picked from commit d5594e2f901ba7eced97b35da8cf0f0a3f6242bd)
parent b14bb41b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -372,6 +372,7 @@ java_library {
        "devicepolicyprotosnano",

        "com.android.sysprop.apex",
        "com.android.sysprop.init",
        "PlatformProperties",
    ],
    sdk_version: "core_platform",
+1 −1
Original line number Diff line number Diff line
@@ -378,7 +378,7 @@ message Atom {
                240 [(module) = "framework"];
        BootTimeEventUtcTime boot_time_event_utc_time_reported = 241;
        BootTimeEventErrorCode boot_time_event_error_code_reported = 242 [(module) = "framework"];
        UserspaceRebootReported userspace_reboot_reported = 243;
        UserspaceRebootReported userspace_reboot_reported = 243 [(module) = "framework"];
        NotificationReported notification_reported = 244 [(module) = "framework"];
        NotificationPanelReported notification_panel_reported = 245;
        NotificationChannelModified notification_panel_modified = 246;
+136 −0
Original line number Diff line number Diff line
/*
 * 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;
        }
    }
}
+23 −0
Original line number Diff line number Diff line
@@ -278,6 +278,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.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -347,6 +348,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;
@@ -2281,6 +2283,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.
@@ -5327,6 +5343,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");
@@ -5347,6 +5369,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                            }
                        }
                    });
            maybeLogUserspaceRebootEvent();
            mUserController.scheduleStartProfiles();
        }
        // UART is on if init's console service is running, send a warning notification.
+7 −2
Original line number Diff line number Diff line
@@ -25,6 +25,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;
@@ -95,6 +96,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;
@@ -3153,7 +3155,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
@@ -5038,7 +5043,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)) {