summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Harshit Mahajan <harshitmahajan@google.com> 2024-09-05 17:49:37 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-09-05 17:49:37 +0000
commit6043d7c246d6abc11e704f62b5ec42707e2f25e5 (patch)
tree3cc876db92d5f909d5a0828297aeb7fff3105559
parent584002d13fd6651adf499a59493285fbf21655fb (diff)
parentc20e5d73cbd39ea580b68ae745453b7ce9a236a1 (diff)
Merge "Separating logging for Apexd triggered rollback" into main
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java4
-rw-r--r--services/core/java/com/android/server/rollback/ApexdRevertLogger.java167
-rw-r--r--services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/ApexdRevertLoggerTest.java114
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java49
5 files changed, 283 insertions, 70 deletions
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 74594cce0041..94bdfbd9c6f5 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -56,8 +56,8 @@ import com.android.server.SystemServiceManager;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUtils;
+import com.android.server.rollback.ApexdRevertLogger;
import com.android.server.rollback.RollbackManagerInternal;
-import com.android.server.rollback.WatchdogRollbackLogger;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -764,7 +764,7 @@ public class StagingManager {
private void logFailedApexSessionsIfNecessary() {
synchronized (mFailedPackageNames) {
if (!mFailedPackageNames.isEmpty()) {
- WatchdogRollbackLogger.logApexdRevert(mContext,
+ ApexdRevertLogger.logApexdRevert(mContext,
mFailedPackageNames, mNativeFailureReason);
}
}
diff --git a/services/core/java/com/android/server/rollback/ApexdRevertLogger.java b/services/core/java/com/android/server/rollback/ApexdRevertLogger.java
new file mode 100644
index 000000000000..9950cc7645b9
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/ApexdRevertLogger.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2024 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.rollback;
+
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
+import android.os.SystemProperties;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class handles the logic for logging Apexd-triggered rollback events.
+ * TODO: b/354112511 Refactor to have a separate metric for ApexdReverts
+ */
+public final class ApexdRevertLogger {
+ private static final String TAG = "WatchdogRollbackLogger";
+
+ private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT";
+
+ /**
+ * Logs that one or more apexd reverts have occurred, along with the crashing native process
+ * that caused apexd to revert during boot.
+ *
+ * @param context the context to use when determining the log packages
+ * @param failedPackageNames a list of names of packages which were reverted
+ * @param failingNativeProcess the crashing native process which caused a revert
+ */
+ public static void logApexdRevert(Context context, @NonNull List<String> failedPackageNames,
+ @NonNull String failingNativeProcess) {
+ Set<VersionedPackage> logPackages = getLogPackages(context, failedPackageNames);
+ for (VersionedPackage logPackage: logPackages) {
+ logEvent(logPackage,
+ failingNativeProcess);
+ }
+ }
+
+ /**
+ * Gets the set of parent packages for a given set of failed package names. In the case that
+ * multiple sessions have failed, we want to log failure for each of the parent packages.
+ * Even if multiple failed packages have the same parent, we only log the parent package once.
+ */
+ private static Set<VersionedPackage> getLogPackages(Context context,
+ @NonNull List<String> failedPackageNames) {
+ Set<VersionedPackage> parentPackages = new ArraySet<>();
+ for (String failedPackageName: failedPackageNames) {
+ parentPackages.add(getLogPackage(context, new VersionedPackage(failedPackageName, 0)));
+ }
+ return parentPackages;
+ }
+
+ /**
+ * Returns the logging parent of a given package if it exists, {@code null} otherwise.
+ *
+ * The logging parent is defined by the {@code android.content.pm.LOGGING_PARENT} field in the
+ * metadata of a package's AndroidManifest.xml.
+ */
+ @VisibleForTesting
+ @Nullable
+ private static VersionedPackage getLogPackage(Context context,
+ @NonNull VersionedPackage failingPackage) {
+ String logPackageName;
+ VersionedPackage loggingParent;
+ logPackageName = getLoggingParentName(context, failingPackage.getPackageName());
+ if (logPackageName == null) {
+ return null;
+ }
+ try {
+ loggingParent = new VersionedPackage(logPackageName, context.getPackageManager()
+ .getPackageInfo(logPackageName, 0 /* flags */).getLongVersionCode());
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ return loggingParent;
+ }
+
+ @Nullable
+ private static String getLoggingParentName(Context context, @NonNull String packageName) {
+ PackageManager packageManager = context.getPackageManager();
+ try {
+ int flags = PackageManager.MATCH_APEX | PackageManager.GET_META_DATA;
+ ApplicationInfo ai = packageManager.getPackageInfo(packageName, flags).applicationInfo;
+ if (ai == null || ai.metaData == null) {
+ return null;
+ }
+ return ai.metaData.getString(LOGGING_PARENT_KEY);
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to discover logging parent package: " + packageName, e);
+ return null;
+ }
+ }
+
+ /**
+ * Log a Apexd rollback event to statsd.
+ *
+ * @param logPackage the package to associate the rollback with.
+ * @param failingPackageName the failing package or process which triggered the rollback.
+ */
+ private static void logEvent(@Nullable VersionedPackage logPackage,
+ @NonNull String failingPackageName) {
+ Slog.i(TAG, "Watchdog event occurred with type: ROLLBACK_SUCCESS"
+ + " logPackage: " + logPackage
+ + " rollbackReason: REASON_NATIVE_CRASH_DURING_BOOT"
+ + " failedPackageName: " + failingPackageName);
+ CrashRecoveryStatsLog.write(
+ WATCHDOG_ROLLBACK_OCCURRED,
+ WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+ (logPackage != null) ? logPackage.getPackageName() : "",
+ (logPackage != null) ? logPackage.getVersionCode() : 0,
+ WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT,
+ failingPackageName,
+ new byte[]{});
+
+ logTestProperties(logPackage, failingPackageName);
+ }
+
+ /**
+ * Writes properties which will be used by rollback tests to check if rollback has occurred
+ * have occurred.
+ *
+ * persist.sys.rollbacktest.enabled: true if rollback tests are running
+ * persist.sys.rollbacktest.ROLLBACK_SUCCESS.logPackage: the package to associate the rollback
+ * persist.sys.rollbacktest.ROLLBACK_SUCCESS.rollbackReason: the reason Apexd triggered it
+ * persist.sys.rollbacktest.ROLLBACK_SUCCESS.failedPackageName: the failing package or process
+ * which triggered the rollback
+ */
+ private static void logTestProperties(@Nullable VersionedPackage logPackage,
+ @NonNull String failingPackageName) {
+ // This property should be on only during the tests
+ final String prefix = "persist.sys.rollbacktest.";
+ if (!SystemProperties.getBoolean(prefix + "enabled", false)) {
+ return;
+ }
+ String key = prefix + "ROLLBACK_SUCCESS";
+ SystemProperties.set(key, String.valueOf(true));
+ SystemProperties.set(key + ".logPackage", logPackage != null ? logPackage.toString() : "");
+ SystemProperties.set(key + ".rollbackReason", "REASON_NATIVE_CRASH_DURING_BOOT");
+ SystemProperties.set(key + ".failedPackageName", failingPackageName);
+ }
+}
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
index 7fc02923bfed..d763199ee019 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -165,25 +165,6 @@ public final class WatchdogRollbackLogger {
}
/**
- * Logs that one or more apexd reverts have occurred, along with the crashing native process
- * that caused apexd to revert during boot.
- *
- * @param context the context to use when determining the log packages
- * @param failedPackageNames a list of names of packages which were reverted
- * @param failingNativeProcess the crashing native process which caused a revert
- */
- public static void logApexdRevert(Context context, @NonNull List<String> failedPackageNames,
- @NonNull String failingNativeProcess) {
- Set<VersionedPackage> logPackages = getLogPackages(context, failedPackageNames);
- for (VersionedPackage logPackage: logPackages) {
- logEvent(logPackage,
- WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
- WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT,
- failingNativeProcess);
- }
- }
-
- /**
* Log a Watchdog rollback event to statsd.
*
* @param logPackage the package to associate the rollback with.
diff --git a/services/tests/servicestests/src/com/android/server/rollback/ApexdRevertLoggerTest.java b/services/tests/servicestests/src/com/android/server/rollback/ApexdRevertLoggerTest.java
new file mode 100644
index 000000000000..4aa6d398f9c7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/rollback/ApexdRevertLoggerTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 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.rollback;
+
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class ApexdRevertLoggerTest {
+
+ private Context mMockContext = mock(Context.class);
+ private PackageManager mMockPm;
+ private PackageInfo mPackageInfo;
+
+ private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT";
+ private static final int PACKAGE_INFO_FLAGS = PackageManager.MATCH_APEX
+ | PackageManager.GET_META_DATA;
+ private static final List<String> sFailingPackages =
+ List.of("package1", "package2", "package3");
+
+ @Before
+ public void setUp() {
+ mMockPm = mock(PackageManager.class);
+ when(mMockContext.getPackageManager()).thenReturn(mMockPm);
+ PackageInstaller mockPi = mock(PackageInstaller.class);
+ when(mMockPm.getPackageInstaller()).thenReturn(mockPi);
+ PackageInstaller.SessionInfo mockSessionInfo = mock(PackageInstaller.SessionInfo.class);
+ when(mockPi.getSessionInfo(anyInt())).thenReturn(mockSessionInfo);
+ mPackageInfo = new PackageInfo();
+ }
+
+ /**
+ * Ensures that we make the correct Package Manager calls in the case that the failing packages
+ * are correctly configured with parent packages.
+ */
+ @Test
+ public void testApexdLoggingCallsWithParents() throws Exception {
+ for (String failingPackage: sFailingPackages) {
+ PackageInfo packageInfo = new PackageInfo();
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ Bundle bundle = new Bundle();
+ bundle.putString(LOGGING_PARENT_KEY, getParent(failingPackage));
+ applicationInfo.metaData = bundle;
+ packageInfo.applicationInfo = applicationInfo;
+ when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
+ }
+
+ when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
+ ApexdRevertLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
+ for (String failingPackage: sFailingPackages) {
+ verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
+ verify(mMockPm, times(1)).getPackageInfo(getParent(failingPackage), 0);
+ }
+ }
+
+ /**
+ * Ensures that we don't make any calls to parent packages in the case that packages are not
+ * correctly configured with parent packages.
+ */
+ @Test
+ public void testApexdLoggingCallsWithNoParents() throws Exception {
+ for (String failingPackage: sFailingPackages) {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
+ }
+ when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
+
+ ApexdRevertLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
+ verify(mMockPm, times(sFailingPackages.size())).getPackageInfo(anyString(), anyInt());
+ for (String failingPackage: sFailingPackages) {
+ verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
+ }
+ }
+
+ private String getParent(String packageName) {
+ return packageName + "-parent";
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
index d1c9643859e3..8257168f8d08 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
@@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -139,52 +138,4 @@ public class WatchdogRollbackLoggerTest {
verify(mMockPm, times(1)).getPackageInfo(
sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
}
-
- /**
- * Ensures that we make the correct Package Manager calls in the case that the failing packages
- * are correctly configured with parent packages.
- */
- @Test
- public void testApexdLoggingCallsWithParents() throws Exception {
- for (String failingPackage: sFailingPackages) {
- PackageInfo packageInfo = new PackageInfo();
- ApplicationInfo applicationInfo = new ApplicationInfo();
- Bundle bundle = new Bundle();
- bundle.putString(LOGGING_PARENT_KEY, getParent(failingPackage));
- applicationInfo.metaData = bundle;
- packageInfo.applicationInfo = applicationInfo;
- when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
- }
-
- when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
- WatchdogRollbackLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
- for (String failingPackage: sFailingPackages) {
- verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
- verify(mMockPm, times(1)).getPackageInfo(getParent(failingPackage), 0);
- }
- }
-
- /**
- * Ensures that we don't make any calls to parent packages in the case that packages are not
- * correctly configured with parent packages.
- */
- @Test
- public void testApexdLoggingCallsWithNoParents() throws Exception {
- for (String failingPackage: sFailingPackages) {
- PackageInfo packageInfo = new PackageInfo();
- packageInfo.applicationInfo = new ApplicationInfo();
- when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
- }
- when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
-
- WatchdogRollbackLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
- verify(mMockPm, times(sFailingPackages.size())).getPackageInfo(anyString(), anyInt());
- for (String failingPackage: sFailingPackages) {
- verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
- }
- }
-
- private String getParent(String packageName) {
- return packageName + "-parent";
- }
}