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

Commit d76ee521 authored by Mohammad Samiul Islam's avatar Mohammad Samiul Islam Committed by Android (Google) Code Review
Browse files

Merge changes from topic "validate-session"

* changes:
  Log watchdog events using logging parent name.
  Allow RPHO save and restore more than one rollback id for logging
parents 996edee3 94f71b56
Loading
Loading
Loading
Loading
+116 −73
Original line number Original line Diff line number Diff line
@@ -28,6 +28,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.content.pm.VersionedPackage;
@@ -45,20 +46,21 @@ import android.util.ArraySet;
import android.util.Slog;
import android.util.Slog;
import android.util.StatsLog;
import android.util.StatsLog;


import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
import com.android.server.PackageWatchdog;
import com.android.server.PackageWatchdog;
import com.android.server.PackageWatchdog.FailureReasons;
import com.android.server.PackageWatchdog.FailureReasons;
import com.android.server.PackageWatchdog.PackageHealthObserver;
import com.android.server.PackageWatchdog.PackageHealthObserver;
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;


import libcore.io.IoUtils;
import java.io.BufferedReader;

import java.io.File;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.List;
import java.util.Set;
import java.util.Set;


@@ -72,11 +74,12 @@ import java.util.Set;
public final class RollbackPackageHealthObserver implements PackageHealthObserver {
public final class RollbackPackageHealthObserver implements PackageHealthObserver {
    private static final String TAG = "RollbackPackageHealthObserver";
    private static final String TAG = "RollbackPackageHealthObserver";
    private static final String NAME = "rollback-observer";
    private static final String NAME = "rollback-observer";
    private static final int INVALID_ROLLBACK_ID = -1;

    private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT";


    private final Context mContext;
    private final Context mContext;
    private final Handler mHandler;
    private final Handler mHandler;
    private final File mLastStagedRollbackIdFile;
    private final File mLastStagedRollbackIdsFile;
    // Staged rollback ids that have been committed but their session is not yet ready
    // Staged rollback ids that have been committed but their session is not yet ready
    @GuardedBy("mPendingStagedRollbackIds")
    @GuardedBy("mPendingStagedRollbackIds")
    private final Set<Integer> mPendingStagedRollbackIds = new ArraySet<>();
    private final Set<Integer> mPendingStagedRollbackIds = new ArraySet<>();
@@ -88,7 +91,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
        mHandler = handlerThread.getThreadHandler();
        mHandler = handlerThread.getThreadHandler();
        File dataDir = new File(Environment.getDataDirectory(), "rollback-observer");
        File dataDir = new File(Environment.getDataDirectory(), "rollback-observer");
        dataDir.mkdirs();
        dataDir.mkdirs();
        mLastStagedRollbackIdFile = new File(dataDir, "last-staged-rollback-id");
        mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids");
        PackageWatchdog.getInstance(mContext).registerHealthObserver(this);
        PackageWatchdog.getInstance(mContext).registerHealthObserver(this);
    }
    }


@@ -150,22 +153,25 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve


    private void onBootCompleted() {
    private void onBootCompleted() {
        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
        PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
        String moduleMetadataPackageName = getModuleMetadataPackageName();

        if (!rollbackManager.getAvailableRollbacks().isEmpty()) {
        if (!rollbackManager.getAvailableRollbacks().isEmpty()) {
            // TODO(gavincorkery): Call into Package Watchdog from outside the observer
            // TODO(gavincorkery): Call into Package Watchdog from outside the observer
            PackageWatchdog.getInstance(mContext).scheduleCheckAndMitigateNativeCrashes();
            PackageWatchdog.getInstance(mContext).scheduleCheckAndMitigateNativeCrashes();
        }
        }


        int rollbackId = popLastStagedRollbackId();
        List<Integer> rollbackIds = popLastStagedRollbackIds();
        if (rollbackId == INVALID_ROLLBACK_ID) {
        Iterator<Integer> rollbackIterator = rollbackIds.iterator();
            // No staged rollback before reboot
        while (rollbackIterator.hasNext()) {
            return;
            int rollbackId = rollbackIterator.next();
            logRollbackStatusOnBoot(rollbackId, rollbackManager.getRecentlyCommittedRollbacks());
        }
        }
    }

    private void logRollbackStatusOnBoot(int rollbackId,
            List<RollbackInfo> recentlyCommittedRollbacks) {
        PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();


        RollbackInfo rollback = null;
        RollbackInfo rollback = null;
        for (RollbackInfo info : rollbackManager.getRecentlyCommittedRollbacks()) {
        for (RollbackInfo info : recentlyCommittedRollbacks) {
            if (rollbackId == info.getRollbackId()) {
            if (rollbackId == info.getRollbackId()) {
                rollback = info;
                rollback = info;
                break;
                break;
@@ -177,13 +183,26 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
            return;
            return;
        }
        }


        // Use the version of the metadata package that was installed before
        // Identify the logging parent for this rollback. When all configurations are correct, each
        // package in the rollback refers to the same logging parent, except for the logging parent
        // itself. If a logging parent is missing for a package, we use the package itself for
        // logging. This might result in over-logging, but we prefer this over no logging.
        final Set<String> loggingPackageNames = new ArraySet<>();
        for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
            final String loggingParentName = getLoggingParentName(packageRollback.getPackageName());
            if (loggingParentName != null) {
                loggingPackageNames.add(loggingParentName);
            } else {
                loggingPackageNames.add(packageRollback.getPackageName());
            }
        }

        // Use the version of the logging parent that was installed before
        // we rolled back for logging purposes.
        // we rolled back for logging purposes.
        VersionedPackage oldLogPackage = null;
        final List<VersionedPackage> oldLoggingPackages = new ArrayList<>();
        for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
        for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
            if (packageRollback.getPackageName().equals(moduleMetadataPackageName)) {
            if (loggingPackageNames.contains(packageRollback.getPackageName())) {
                oldLogPackage = packageRollback.getVersionRolledBackFrom();
                oldLoggingPackages.add(packageRollback.getVersionRolledBackFrom());
                break;
            }
            }
        }
        }


@@ -193,18 +212,19 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
            Slog.e(TAG, "On boot completed, could not load session id " + sessionId);
            Slog.e(TAG, "On boot completed, could not load session id " + sessionId);
            return;
            return;
        }
        }

        for (VersionedPackage oldLoggingPackage : oldLoggingPackages) {
            if (sessionInfo.isStagedSessionApplied()) {
            if (sessionInfo.isStagedSessionApplied()) {
            logEvent(oldLogPackage,
                logEvent(oldLoggingPackage,
                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
                        WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
                        WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
        } else if (sessionInfo.isStagedSessionReady()) {
            } else if (sessionInfo.isStagedSessionFailed()) {
            // TODO: What do for staged session ready but not applied
                logEvent(oldLoggingPackage,
        } else {
            logEvent(oldLogPackage,
                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
                        WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
                        WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
            }
            }
        }
        }
    }


    private RollbackInfo getAvailableRollback(VersionedPackage failedPackage) {
    private RollbackInfo getAvailableRollback(VersionedPackage failedPackage) {
        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
@@ -236,27 +256,17 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
    }
    }


    @Nullable
    @Nullable
    private String getModuleMetadataPackageName() {
    private String getLoggingParentName(String packageName) {
        String packageName = mContext.getResources().getString(
        PackageManager packageManager = mContext.getPackageManager();
                R.string.config_defaultModuleMetadataProvider);
        try {
        if (TextUtils.isEmpty(packageName)) {
            ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
            return null;
                    PackageManager.GET_META_DATA);
        }
            if (ai.metaData == null) {
        return packageName;
    }

    @Nullable
    private VersionedPackage getModuleMetadataPackage() {
        String packageName = getModuleMetadataPackageName();
        if (packageName == null) {
                return null;
                return null;
            }
            }

            return ai.metaData.getString(LOGGING_PARENT_KEY);
        try {
        } catch (Exception e) {
            return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo(
            Slog.w(TAG, "Unable to discover logging parent package: " + packageName, e);
                            packageName, 0 /* flags */).getLongVersionCode());
        } catch (PackageManager.NameNotFoundException e) {
            Slog.w(TAG, "Module metadata provider not found");
            return null;
            return null;
        }
        }
    }
    }
@@ -294,7 +304,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
                    if (logPackage != null) {
                    if (logPackage != null) {
                        // We save the rollback id so that after reboot, we can log if rollback was
                        // We save the rollback id so that after reboot, we can log if rollback was
                        // successful or not. If logPackage is null, then there is nothing to log.
                        // successful or not. If logPackage is null, then there is nothing to log.
                        saveLastStagedRollbackId(rollbackId);
                        saveStagedRollbackId(rollbackId);
                    }
                    }
                    logEvent(logPackage,
                    logEvent(logPackage,
                            StatsLog
                            StatsLog
@@ -338,34 +348,39 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
        }
        }
    }
    }


    private void saveLastStagedRollbackId(int stagedRollbackId) {
    private void saveStagedRollbackId(int stagedRollbackId) {
        try {
        try {
            FileOutputStream fos = new FileOutputStream(mLastStagedRollbackIdFile);
            FileOutputStream fos = new FileOutputStream(
                    mLastStagedRollbackIdsFile, /*append*/true);
            PrintWriter pw = new PrintWriter(fos);
            PrintWriter pw = new PrintWriter(fos);
            pw.println(stagedRollbackId);
            pw.append(",").append(String.valueOf(stagedRollbackId));
            pw.flush();
            pw.flush();
            FileUtils.sync(fos);
            FileUtils.sync(fos);
            pw.close();
            pw.close();
        } catch (IOException e) {
        } catch (IOException e) {
            Slog.e(TAG, "Failed to save last staged rollback id", e);
            Slog.e(TAG, "Failed to save last staged rollback id", e);
            mLastStagedRollbackIdFile.delete();
            mLastStagedRollbackIdsFile.delete();
        }
        }
    }
    }


    private int popLastStagedRollbackId() {
    private List<Integer> popLastStagedRollbackIds() {
        int rollbackId = INVALID_ROLLBACK_ID;
        try (BufferedReader reader =
        if (!mLastStagedRollbackIdFile.exists()) {
                     new BufferedReader(new FileReader(mLastStagedRollbackIdsFile))) {
            return rollbackId;
            String line = reader.readLine();
            // line is of format : ",id1,id2,id3....,idn"
            String[] sessionIdsStr = line.split(",");
            ArrayList<Integer> result = new ArrayList<>();
            for (String sessionIdStr: sessionIdsStr) {
                if (!TextUtils.isEmpty(sessionIdStr.trim())) {
                    result.add(Integer.parseInt(sessionIdStr));
                }
                }

        try {
            rollbackId = Integer.parseInt(
                    IoUtils.readFileAsString(mLastStagedRollbackIdFile.getAbsolutePath()).trim());
        } catch (IOException | NumberFormatException e) {
            Slog.e(TAG, "Failed to retrieve last staged rollback id", e);
            }
            }
        mLastStagedRollbackIdFile.delete();
            return result;
        return rollbackId;
        } catch (Exception ignore) {
            return Collections.emptyList();
        } finally {
            mLastStagedRollbackIdsFile.delete();
        }
    }
    }


    private static String rollbackTypeToString(int type) {
    private static String rollbackTypeToString(int type) {
@@ -383,16 +398,33 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
        }
        }
    }
    }


    private static String rollbackReasonToString(int reason) {
        switch (reason) {
            case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH:
                return "REASON_NATIVE_CRASH";
            case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK:
                return "REASON_EXPLICIT_HEALTH_CHECK";
            case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH:
                return "REASON_APP_CRASH";
            case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING:
                return "REASON_APP_NOT_RESPONDING";
            default:
                return "UNKNOWN";
        }
    }

    private static void logEvent(@Nullable VersionedPackage logPackage, int type,
    private static void logEvent(@Nullable VersionedPackage logPackage, int type,
            int rollbackReason, @NonNull String failingPackageName) {
            int rollbackReason, @NonNull String failingPackageName) {
        Slog.i(TAG, "Watchdog event occurred of type: " + rollbackTypeToString(type));
        Slog.i(TAG, "Watchdog event occurred with type: " + rollbackTypeToString(type)
                + " logPackage: " + logPackage
                + " rollbackReason: " + rollbackReasonToString(rollbackReason)
                + " failedPackageName: " + failingPackageName);
        if (logPackage != null) {
        if (logPackage != null) {
            StatsLog.logWatchdogRollbackOccurred(type, logPackage.getPackageName(),
            StatsLog.logWatchdogRollbackOccurred(type, logPackage.getPackageName(),
                    logPackage.getVersionCode(), rollbackReason, failingPackageName);
                    logPackage.getLongVersionCode(), rollbackReason, failingPackageName);
        }
        }
    }
    }



    /**
    /**
     * Returns true if the package name is the name of a module.
     * Returns true if the package name is the name of a module.
     */
     */
@@ -422,10 +454,21 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
        } else {
        } else {
            failedPackageToLog = failedPackage.getPackageName();
            failedPackageToLog = failedPackage.getPackageName();
        }
        }
        final VersionedPackage logPackage = isModule(failedPackage.getPackageName())
        VersionedPackage logPackageTemp = null;
                ? getModuleMetadataPackage()
        if (isModule(failedPackage.getPackageName())) {
                : null;
            String logPackageName = getLoggingParentName(failedPackage.getPackageName());
            if (logPackageName == null) {
                logPackageName = failedPackage.getPackageName();
            }
            try {
                logPackageTemp = new VersionedPackage(logPackageName, mContext.getPackageManager()
                        .getPackageInfo(logPackageName, 0 /* flags */).getLongVersionCode());
            } catch (PackageManager.NameNotFoundException e) {
                logPackageTemp = null;
            }
        }


        final VersionedPackage logPackage = logPackageTemp;
        logEvent(logPackage,
        logEvent(logPackage,
                StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
                StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
                reasonToLog, failedPackageToLog);
                reasonToLog, failedPackageToLog);