summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java39
-rw-r--r--services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java72
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java86
3 files changed, 172 insertions, 25 deletions
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 89354537526c..614cc3fc2f3a 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -57,6 +57,7 @@ import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
+import android.text.TextUtils;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -67,6 +68,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageHelper;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
+import com.android.server.rollback.WatchdogRollbackLogger;
import java.io.File;
import java.io.IOException;
@@ -99,6 +101,10 @@ public class StagingManager {
@GuardedBy("mStagedSessions")
private final SparseIntArray mSessionRollbackIds = new SparseIntArray();
+ @GuardedBy("mFailedPackageNames")
+ private final List<String> mFailedPackageNames = new ArrayList<>();
+ private String mNativeFailureReason;
+
StagingManager(PackageInstallerService pi, Context context) {
mPi = pi;
mContext = context;
@@ -441,6 +447,22 @@ public class StagingManager {
}
}
+ /**
+ * Prepares for the logging of apexd reverts by storing the native failure reason if necessary,
+ * and adding the package name of the session which apexd reverted to the list of reverted
+ * session package names.
+ * Logging needs to wait until the ACTION_BOOT_COMPLETED broadcast is sent.
+ */
+ private void prepareForLoggingApexdRevert(@NonNull PackageInstallerSession session,
+ @NonNull String nativeFailureReason) {
+ synchronized (mFailedPackageNames) {
+ mNativeFailureReason = nativeFailureReason;
+ if (session.getPackageName() != null) {
+ mFailedPackageNames.add(session.getPackageName());
+ }
+ }
+ }
+
private void resumeSession(@NonNull PackageInstallerSession session) {
Slog.d(TAG, "Resuming session " + session.sessionId);
@@ -450,6 +472,12 @@ public class StagingManager {
// Check with apexservice whether the apex packages have been activated.
apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId);
+ // Prepare for logging a native crash during boot, if one occurred.
+ if (apexSessionInfo != null && !TextUtils.isEmpty(
+ apexSessionInfo.crashingNativeProcess)) {
+ prepareForLoggingApexdRevert(session, apexSessionInfo.crashingNativeProcess);
+ }
+
if (apexSessionInfo != null && apexSessionInfo.isVerified) {
// Session has been previously submitted to apexd, but didn't complete all the
// pre-reboot verification, perhaps because the device rebooted in the meantime.
@@ -955,12 +983,23 @@ public class StagingManager {
}
}
+ private void logFailedApexSessionsIfNecessary() {
+ synchronized (mFailedPackageNames) {
+ if (!mFailedPackageNames.isEmpty()) {
+ WatchdogRollbackLogger.logApexdRevert(mContext,
+ mFailedPackageNames, mNativeFailureReason);
+ }
+ }
+ }
+
void systemReady() {
// Register the receiver of boot completed intent for staging manager.
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context ctx, Intent intent) {
mPreRebootVerificationHandler.readyToStart();
+ BackgroundThread.getExecutor().execute(
+ () -> logFailedApexSessionsIfNecessary());
ctx.unregisterReceiver(this);
}
}, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
index 46ec2f8258ca..f3f14a95eac6 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -20,7 +20,12 @@ import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCU
import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING;
import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK;
import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT;
import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -36,7 +41,6 @@ import android.util.Slog;
import android.util.StatsLog;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.PackageWatchdog;
import java.util.ArrayList;
@@ -58,8 +62,8 @@ public final class WatchdogRollbackLogger {
private static String getLoggingParentName(Context context, @NonNull String packageName) {
PackageManager packageManager = context.getPackageManager();
try {
- ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
- PackageManager.GET_META_DATA);
+ int flags = PackageManager.MATCH_APEX | PackageManager.GET_META_DATA;
+ ApplicationInfo ai = packageManager.getPackageInfo(packageName, flags).applicationInfo;
if (ai.metaData == null) {
return null;
}
@@ -95,6 +99,22 @@ public final class WatchdogRollbackLogger {
return loggingParent;
}
+
+ /**
+ * 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;
+ }
+
+
static void logRollbackStatusOnBoot(Context context, int rollbackId,
List<RollbackInfo> recentlyCommittedRollbacks) {
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
@@ -142,19 +162,36 @@ public final class WatchdogRollbackLogger {
for (VersionedPackage oldLoggingPackage : oldLoggingPackages) {
if (sessionInfo.isStagedSessionApplied()) {
logEvent(oldLoggingPackage,
- FrameworkStatsLog
- .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+ WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
} else if (sessionInfo.isStagedSessionFailed()) {
logEvent(oldLoggingPackage,
- FrameworkStatsLog
- .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+ WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
}
}
}
/**
+ * 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.
@@ -196,14 +233,13 @@ public final class WatchdogRollbackLogger {
private static String rollbackTypeToString(int type) {
switch (type) {
- case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
return "ROLLBACK_INITIATE";
- case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
return "ROLLBACK_SUCCESS";
- case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE:
return "ROLLBACK_FAILURE";
- case FrameworkStatsLog
- .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED:
return "ROLLBACK_BOOT_TRIGGERED";
default:
return "UNKNOWN";
@@ -212,16 +248,16 @@ public final class WatchdogRollbackLogger {
private static String rollbackReasonToString(int reason) {
switch (reason) {
- case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH:
return "REASON_NATIVE_CRASH";
- case FrameworkStatsLog
- .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK:
return "REASON_EXPLICIT_HEALTH_CHECK";
- case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH:
return "REASON_APP_CRASH";
- case FrameworkStatsLog
- .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING:
return "REASON_APP_NOT_RESPONDING";
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT:
+ return "REASON_NATIVE_CRASH_DURING_BOOT";
default:
return "UNKNOWN";
}
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 ba493d4f9646..d1c9643859e3 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,11 @@ 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;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -36,6 +40,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.List;
+
@RunWith(JUnit4.class)
public class WatchdogRollbackLoggerTest {
@@ -46,6 +52,11 @@ public class WatchdogRollbackLoggerTest {
private PackageInfo mPackageInfo;
private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT";
+ private static final String LOGGING_PARENT_VALUE = "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() {
@@ -64,10 +75,12 @@ public class WatchdogRollbackLoggerTest {
*/
@Test
public void testLogPackageHasNoMetadata() throws Exception {
- when(mMockPm.getApplicationInfo(anyString(), anyInt())).thenReturn(mApplicationInfo);
+ when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
sTestPackageV1);
assertThat(logPackage).isNull();
+ verify(mMockPm, times(1)).getPackageInfo(
+ sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
}
/**
@@ -76,12 +89,16 @@ public class WatchdogRollbackLoggerTest {
*/
@Test
public void testLogPackageParentKeyIsNull() throws Exception {
- when(mMockPm.getApplicationInfo(anyString(), anyInt())).thenReturn(mApplicationInfo);
+ when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
Bundle bundle = new Bundle();
bundle.putString(LOGGING_PARENT_KEY, null);
+ mApplicationInfo.metaData = bundle;
+ mPackageInfo.applicationInfo = mApplicationInfo;
VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
sTestPackageV1);
assertThat(logPackage).isNull();
+ verify(mMockPm, times(1)).getPackageInfo(
+ sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
}
/**
@@ -90,15 +107,18 @@ public class WatchdogRollbackLoggerTest {
@Test
public void testLogPackageHasParentKey() throws Exception {
Bundle bundle = new Bundle();
- bundle.putString(LOGGING_PARENT_KEY, "logging.parent");
+ bundle.putString(LOGGING_PARENT_KEY, LOGGING_PARENT_VALUE);
mApplicationInfo.metaData = bundle;
+ mPackageInfo.applicationInfo = mApplicationInfo;
mPackageInfo.setLongVersionCode(12345L);
- when(mMockPm.getApplicationInfo(anyString(), anyInt())).thenReturn(mApplicationInfo);
when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
sTestPackageV1);
- VersionedPackage expectedLogPackage = new VersionedPackage("logging.parent", 12345);
+ VersionedPackage expectedLogPackage = new VersionedPackage(LOGGING_PARENT_VALUE, 12345);
assertThat(logPackage).isEqualTo(expectedLogPackage);
+ verify(mMockPm, times(1)).getPackageInfo(
+ sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
+
}
/**
@@ -107,12 +127,64 @@ public class WatchdogRollbackLoggerTest {
@Test
public void testLogPackageNameNotFound() throws Exception {
Bundle bundle = new Bundle();
- bundle.putString("android.content.pm.LOGGING_PARENT", "logging.parent");
+ bundle.putString(LOGGING_PARENT_KEY, LOGGING_PARENT_VALUE);
mApplicationInfo.metaData = bundle;
- when(mMockPm.getPackageInfo(anyString(), anyInt())).thenThrow(
+ mPackageInfo.applicationInfo = mApplicationInfo;
+ when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
+ when(mMockPm.getPackageInfo(same(LOGGING_PARENT_VALUE), anyInt())).thenThrow(
new PackageManager.NameNotFoundException());
VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
sTestPackageV1);
assertThat(logPackage).isNull();
+ 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";
}
}