Loading services/core/java/com/android/server/pm/StagingManager.java +39 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading @@ -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. Loading Loading @@ -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)); Loading services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java +54 −18 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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(); Loading Loading @@ -142,18 +162,35 @@ 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. * Loading Loading @@ -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"; Loading @@ -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"; } Loading services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java +79 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 { Loading @@ -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() { Loading @@ -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); } /** Loading @@ -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); } /** Loading @@ -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); } /** Loading @@ -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"; } } Loading
services/core/java/com/android/server/pm/StagingManager.java +39 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading @@ -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. Loading Loading @@ -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)); Loading
services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java +54 −18 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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(); Loading Loading @@ -142,18 +162,35 @@ 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. * Loading Loading @@ -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"; Loading @@ -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"; } Loading
services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java +79 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 { Loading @@ -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() { Loading @@ -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); } /** Loading @@ -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); } /** Loading @@ -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); } /** Loading @@ -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"; } }