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

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

Merge changes from topic "native-crash"

* changes:
  Mitigate native crash without depending on ModuleMetaData
  Rollback everything that is available when crash is unattributable
parents dda7e6a9 a3f650dc
Loading
Loading
Loading
Loading
+96 −48
Original line number Diff line number Diff line
@@ -103,8 +103,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve

    @Override
    public int onHealthCheckFailed(VersionedPackage failedPackage) {
        if (getAvailableRollback(mContext.getSystemService(RollbackManager.class), failedPackage)
                == null) {
        if (getAvailableRollback(failedPackage) == null) {
            // Don't handle the notification, no rollbacks available for the package
            return PackageHealthObserverImpact.USER_IMPACT_NONE;
        } else {
@@ -115,51 +114,14 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve

    @Override
    public boolean execute(VersionedPackage failedPackage, @FailureReasons int rollbackReason) {
        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
        VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
        RollbackInfo rollback = getAvailableRollback(rollbackManager, failedPackage);
        int reasonToLog = mapFailureReasonToMetric(rollbackReason);

        RollbackInfo rollback = getAvailableRollback(failedPackage);
        if (rollback == null) {
            Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ "
                    + failedPackage.getPackageName() + "] with versionCode: ["
                    + failedPackage.getVersionCode() + "]");
            return false;
        }

        logEvent(moduleMetadataPackage,
                StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
                reasonToLog, failedPackage.getPackageName());
        LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
            int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
                    RollbackManager.STATUS_FAILURE);
            if (status == RollbackManager.STATUS_SUCCESS) {
                if (rollback.isStaged()) {
                    int rollbackId = rollback.getRollbackId();
                    synchronized (mPendingStagedRollbackIds) {
                        mPendingStagedRollbackIds.add(rollbackId);
                    }
                    BroadcastReceiver listener =
                            listenForStagedSessionReady(rollbackManager, rollbackId,
                                    moduleMetadataPackage);
                    handleStagedSessionChange(rollbackManager, rollbackId, listener,
                            moduleMetadataPackage);
                } else {
                    logEvent(moduleMetadataPackage,
                            StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
                            reasonToLog, failedPackage.getPackageName());
                }
            } else {
                logEvent(moduleMetadataPackage,
                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
                        reasonToLog, failedPackage.getPackageName());
            }
        });

        mHandler.post(() ->
                rollbackManager.commitRollback(rollback.getRollbackId(),
                    Collections.singletonList(failedPackage),
                    rollbackReceiver.getIntentSender()));
        rollbackPackage(rollback, failedPackage, rollbackReason);
        // Assume rollback executed successfully
        return true;
    }
@@ -188,9 +150,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
        PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
        String moduleMetadataPackageName = getModuleMetadataPackageName();
        VersionedPackage newModuleMetadataPackage = getModuleMetadataPackage();

        if (getAvailableRollback(rollbackManager, newModuleMetadataPackage) != null) {
        if (!rollbackManager.getAvailableRollbacks().isEmpty()) {
            scheduleCheckAndMitigateNativeCrashes();
        }

@@ -242,8 +203,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
        }
    }

    private RollbackInfo getAvailableRollback(RollbackManager rollbackManager,
            VersionedPackage failedPackage) {
    private RollbackInfo getAvailableRollback(VersionedPackage failedPackage) {
        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
        for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
            for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
                boolean hasFailedPackage = packageRollback.getPackageName().equals(
@@ -285,7 +246,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
    }

    private BroadcastReceiver listenForStagedSessionReady(RollbackManager rollbackManager,
            int rollbackId, VersionedPackage moduleMetadataPackage) {
            int rollbackId, @Nullable VersionedPackage moduleMetadataPackage) {
        BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
@@ -300,7 +261,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
    }

    private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId,
            BroadcastReceiver listener, VersionedPackage moduleMetadataPackage) {
            BroadcastReceiver listener, @Nullable VersionedPackage moduleMetadataPackage) {
        PackageInstaller packageInstaller =
                mContext.getPackageManager().getPackageInstaller();
        List<RollbackInfo> recentRollbacks =
@@ -390,7 +351,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
        mNumberOfNativeCrashPollsRemaining--;
        // Check if native watchdog reported a crash
        if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) {
            execute(getModuleMetadataPackage(), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
            rollbackAll();
            // we stop polling after an attempt to execute rollback, regardless of whether the
            // attempt succeeds or not
        } else {
@@ -401,6 +362,93 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
        }
    }

    /**
     * Returns true if the package name is the name of a module.
     */
    private boolean isModule(String packageName) {
        PackageManager pm = mContext.getPackageManager();
        try {
            return pm.getModuleInfo(packageName, 0) != null;
        } catch (PackageManager.NameNotFoundException ignore) {
            return false;
        }
    }

    private VersionedPackage getVersionedPackage(String packageName) {
        try {
            return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo(
                    packageName, 0 /* flags */).getLongVersionCode());
        } catch (PackageManager.NameNotFoundException e) {
            return null;
        }
    }

    /**
     * Rolls back the session that owns {@code failedPackage}
     *
     * @param rollback {@code rollbackInfo} of the {@code failedPackage}
     * @param failedPackage the package that needs to be rolled back
     */
    private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage,
            @FailureReasons int rollbackReason) {
        final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
        int reasonToLog = mapFailureReasonToMetric(rollbackReason);
        final VersionedPackage logPackage = isModule(failedPackage.getPackageName())
                ? getModuleMetadataPackage()
                : null;

        logEvent(logPackage,
                StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
                reasonToLog, failedPackage.getPackageName());
        final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
            int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
                    RollbackManager.STATUS_FAILURE);
            if (status == RollbackManager.STATUS_SUCCESS) {
                if (rollback.isStaged()) {
                    int rollbackId = rollback.getRollbackId();
                    synchronized (mPendingStagedRollbackIds) {
                        mPendingStagedRollbackIds.add(rollbackId);
                    }
                    BroadcastReceiver listener =
                            listenForStagedSessionReady(rollbackManager, rollbackId,
                                    logPackage);
                    handleStagedSessionChange(rollbackManager, rollbackId, listener,
                            logPackage);
                } else {
                    logEvent(logPackage,
                            StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
                            reasonToLog, failedPackage.getPackageName());
                }
            } else {
                logEvent(logPackage,
                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
                        reasonToLog, failedPackage.getPackageName());
            }
        });

        mHandler.post(() ->
                rollbackManager.commitRollback(rollback.getRollbackId(),
                        Collections.singletonList(failedPackage),
                        rollbackReceiver.getIntentSender()));
    }

    private void rollbackAll() {
        Slog.i(TAG, "Rolling back all available rollbacks");
        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
        List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks();

        for (RollbackInfo rollback : rollbacks) {
            String samplePackageName = rollback.getPackages().get(0).getPackageName();
            VersionedPackage sampleVersionedPackage = getVersionedPackage(samplePackageName);
            if (sampleVersionedPackage == null) {
                Slog.e(TAG, "Failed to rollback " + samplePackageName);
                continue;
            }
            rollbackPackage(rollback, sampleVersionedPackage,
                    PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
        }
    }

    /**
     * Since this method can eventually trigger a RollbackManager rollback, it should be called
     * only once boot has completed {@code onBootCompleted} and not earlier, because the install
+10 −40
Original line number Diff line number Diff line
@@ -22,18 +22,14 @@ import static com.android.cts.rollback.lib.RollbackUtils.getUniqueRollbackInfoFo
import static com.google.common.truth.Truth.assertThat;

import android.Manifest;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.os.ParcelFileDescriptor;
import android.provider.DeviceConfig;
import android.text.TextUtils;

import androidx.test.platform.app.InstrumentationRegistry;

@@ -44,7 +40,6 @@ import com.android.cts.install.lib.TestApp;
import com.android.cts.install.lib.Uninstall;
import com.android.cts.rollback.lib.Rollback;
import com.android.cts.rollback.lib.RollbackUtils;
import com.android.internal.R;

import libcore.io.IoUtils;

@@ -75,7 +70,6 @@ public class StagedRollbackTest {
    private static final String PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS =
            "watchdog_request_timeout_millis";

    private static final String MODULE_META_DATA_PACKAGE = getModuleMetadataPackageName();
    private static final TestApp NETWORK_STACK = new TestApp("NetworkStack",
            getNetworkStackPackageName(), -1, false, findNetworkStackApk());

@@ -186,21 +180,15 @@ public class StagedRollbackTest {
    }

    /**
     * Stage install ModuleMetadata package to simulate a Mainline module update.
     * Stage install an apk with rollback that will be later triggered by unattributable crash.
     */
    @Test
    public void testNativeWatchdogTriggersRollback_Phase1() throws Exception {
        resetModuleMetadataPackage();
        Context context = InstrumentationRegistry.getInstrumentation().getContext();
        PackageInfo metadataPackageInfo = context.getPackageManager().getPackageInfo(
                MODULE_META_DATA_PACKAGE, 0);
        String metadataApkPath = metadataPackageInfo.applicationInfo.sourceDir;
        assertThat(metadataApkPath).isNotNull();
        assertThat(metadataApkPath).isNotEqualTo("");

        runShellCommand("pm install "
                + "-r --enable-rollback --staged --wait "
                + metadataApkPath);
        Uninstall.packages(TestApp.A);
        Install.single(TestApp.A1).commit();
        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);

        Install.single(TestApp.A2).setEnableRollback().setStaged().commit();
    }

    /**
@@ -208,9 +196,10 @@ public class StagedRollbackTest {
     */
    @Test
    public void testNativeWatchdogTriggersRollback_Phase2() throws Exception {
        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
        RollbackManager rm = RollbackUtils.getRollbackManager();
        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
                        MODULE_META_DATA_PACKAGE)).isNotNull();
                TestApp.A)).isNotNull();
    }

    /**
@@ -218,9 +207,10 @@ public class StagedRollbackTest {
     */
    @Test
    public void testNativeWatchdogTriggersRollback_Phase3() throws Exception {
        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
        RollbackManager rm = RollbackUtils.getRollbackManager();
        assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
                        MODULE_META_DATA_PACKAGE)).isNotNull();
                TestApp.A)).isNotNull();
    }

    @Test
@@ -351,26 +341,6 @@ public class StagedRollbackTest {
                        getNetworkStackPackageName())).isNull();
    }

    @Nullable
    private static String getModuleMetadataPackageName() {
        String packageName = InstrumentationRegistry.getInstrumentation().getContext()
                .getResources().getString(R.string.config_defaultModuleMetadataProvider);
        if (TextUtils.isEmpty(packageName)) {
            return null;
        }
        return packageName;
    }

    private void resetModuleMetadataPackage() {
        RollbackManager rm = RollbackUtils.getRollbackManager();

        assertThat(MODULE_META_DATA_PACKAGE).isNotNull();
        rm.expireRollbackForPackage(MODULE_META_DATA_PACKAGE);

        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
                MODULE_META_DATA_PACKAGE)).isNull();
    }

    private static void runShellCommand(String cmd) {
        ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
                .executeShellCommand(cmd);