1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.rollback; 18 19 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; 20 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; 21 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK; 22 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH; 23 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT; 24 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN; 25 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED; 26 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE; 27 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE; 28 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS; 29 30 import android.annotation.NonNull; 31 import android.annotation.Nullable; 32 import android.content.Context; 33 import android.content.pm.ApplicationInfo; 34 import android.content.pm.PackageInstaller; 35 import android.content.pm.PackageManager; 36 import android.content.pm.VersionedPackage; 37 import android.content.rollback.PackageRollbackInfo; 38 import android.content.rollback.RollbackInfo; 39 import android.os.SystemProperties; 40 import android.text.TextUtils; 41 import android.util.ArraySet; 42 import android.util.Slog; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.internal.util.FrameworkStatsLog; 46 import com.android.server.PackageWatchdog; 47 48 import java.util.List; 49 import java.util.Set; 50 51 /** 52 * This class handles the logic for logging Watchdog-triggered rollback events. 53 */ 54 public final class WatchdogRollbackLogger { 55 private static final String TAG = "WatchdogRollbackLogger"; 56 57 private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT"; 58 WatchdogRollbackLogger()59 private WatchdogRollbackLogger() { 60 } 61 62 @Nullable getLoggingParentName(Context context, @NonNull String packageName)63 private static String getLoggingParentName(Context context, @NonNull String packageName) { 64 PackageManager packageManager = context.getPackageManager(); 65 try { 66 int flags = PackageManager.MATCH_APEX | PackageManager.GET_META_DATA; 67 ApplicationInfo ai = packageManager.getPackageInfo(packageName, flags).applicationInfo; 68 if (ai.metaData == null) { 69 return null; 70 } 71 return ai.metaData.getString(LOGGING_PARENT_KEY); 72 } catch (Exception e) { 73 Slog.w(TAG, "Unable to discover logging parent package: " + packageName, e); 74 return null; 75 } 76 } 77 78 /** 79 * Returns the logging parent of a given package if it exists, {@code null} otherwise. 80 * 81 * The logging parent is defined by the {@code android.content.pm.LOGGING_PARENT} field in the 82 * metadata of a package's AndroidManifest.xml. 83 */ 84 @VisibleForTesting 85 @Nullable getLogPackage(Context context, @NonNull VersionedPackage failingPackage)86 static VersionedPackage getLogPackage(Context context, 87 @NonNull VersionedPackage failingPackage) { 88 String logPackageName; 89 VersionedPackage loggingParent; 90 logPackageName = getLoggingParentName(context, failingPackage.getPackageName()); 91 if (logPackageName == null) { 92 return null; 93 } 94 try { 95 loggingParent = new VersionedPackage(logPackageName, context.getPackageManager() 96 .getPackageInfo(logPackageName, 0 /* flags */).getLongVersionCode()); 97 } catch (PackageManager.NameNotFoundException e) { 98 return null; 99 } 100 return loggingParent; 101 } 102 103 104 /** 105 * Gets the set of parent packages for a given set of failed package names. In the case that 106 * multiple sessions have failed, we want to log failure for each of the parent packages. 107 * Even if multiple failed packages have the same parent, we only log the parent package once. 108 */ getLogPackages(Context context, @NonNull List<String> failedPackageNames)109 private static Set<VersionedPackage> getLogPackages(Context context, 110 @NonNull List<String> failedPackageNames) { 111 Set<VersionedPackage> parentPackages = new ArraySet<>(); 112 for (String failedPackageName: failedPackageNames) { 113 parentPackages.add(getLogPackage(context, new VersionedPackage(failedPackageName, 0))); 114 } 115 return parentPackages; 116 } 117 118 logRollbackStatusOnBoot(Context context, int rollbackId, String logPackageName, List<RollbackInfo> recentlyCommittedRollbacks)119 static void logRollbackStatusOnBoot(Context context, int rollbackId, String logPackageName, 120 List<RollbackInfo> recentlyCommittedRollbacks) { 121 PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); 122 123 RollbackInfo rollback = null; 124 for (RollbackInfo info : recentlyCommittedRollbacks) { 125 if (rollbackId == info.getRollbackId()) { 126 rollback = info; 127 break; 128 } 129 } 130 131 if (rollback == null) { 132 Slog.e(TAG, "rollback info not found for last staged rollback: " + rollbackId); 133 return; 134 } 135 136 // Use the version of the logging parent that was installed before 137 // we rolled back for logging purposes. 138 VersionedPackage oldLoggingPackage = null; 139 if (!TextUtils.isEmpty(logPackageName)) { 140 for (PackageRollbackInfo packageRollback : rollback.getPackages()) { 141 if (logPackageName.equals(packageRollback.getPackageName())) { 142 oldLoggingPackage = packageRollback.getVersionRolledBackFrom(); 143 break; 144 } 145 } 146 } 147 148 int sessionId = rollback.getCommittedSessionId(); 149 PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId); 150 if (sessionInfo == null) { 151 Slog.e(TAG, "On boot completed, could not load session id " + sessionId); 152 return; 153 } 154 155 if (sessionInfo.isStagedSessionApplied()) { 156 logEvent(oldLoggingPackage, 157 WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, 158 WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); 159 } else if (sessionInfo.isStagedSessionFailed()) { 160 logEvent(oldLoggingPackage, 161 WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, 162 WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); 163 } 164 } 165 166 /** 167 * Logs that one or more apexd reverts have occurred, along with the crashing native process 168 * that caused apexd to revert during boot. 169 * 170 * @param context the context to use when determining the log packages 171 * @param failedPackageNames a list of names of packages which were reverted 172 * @param failingNativeProcess the crashing native process which caused a revert 173 */ logApexdRevert(Context context, @NonNull List<String> failedPackageNames, @NonNull String failingNativeProcess)174 public static void logApexdRevert(Context context, @NonNull List<String> failedPackageNames, 175 @NonNull String failingNativeProcess) { 176 Set<VersionedPackage> logPackages = getLogPackages(context, failedPackageNames); 177 for (VersionedPackage logPackage: logPackages) { 178 logEvent(logPackage, 179 WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, 180 WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT, 181 failingNativeProcess); 182 } 183 } 184 185 /** 186 * Log a Watchdog rollback event to statsd. 187 * 188 * @param logPackage the package to associate the rollback with. 189 * @param type the state of the rollback. 190 * @param rollbackReason the reason Watchdog triggered a rollback, if known. 191 * @param failingPackageName the failing package or process which triggered the rollback. 192 */ logEvent(@ullable VersionedPackage logPackage, int type, int rollbackReason, @NonNull String failingPackageName)193 public static void logEvent(@Nullable VersionedPackage logPackage, int type, 194 int rollbackReason, @NonNull String failingPackageName) { 195 Slog.i(TAG, "Watchdog event occurred with type: " + rollbackTypeToString(type) 196 + " logPackage: " + logPackage 197 + " rollbackReason: " + rollbackReasonToString(rollbackReason) 198 + " failedPackageName: " + failingPackageName); 199 if (logPackage != null) { 200 FrameworkStatsLog.write( 201 FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED, 202 type, 203 logPackage.getPackageName(), 204 logPackage.getVersionCode(), 205 rollbackReason, 206 failingPackageName, 207 new byte[]{}); 208 } else { 209 // In the case that the log package is null, still log an empty string as an 210 // indication that retrieving the logging parent failed. 211 FrameworkStatsLog.write( 212 FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED, 213 type, 214 "", 215 0, 216 rollbackReason, 217 failingPackageName, 218 new byte[]{}); 219 } 220 221 logTestProperties(logPackage, type, rollbackReason, failingPackageName); 222 } 223 224 /** 225 * Writes properties which will be used by rollback tests to check if particular rollback 226 * events have occurred. 227 * 228 * persist.sys.rollbacktest.enabled: true if rollback tests are running 229 * persist.sys.rollbacktest.EVENT_TYPE: true if a particular rollback event has occurred 230 * ex: persist.sys.rollbacktest.ROLLBACK_INITIATE is true if ROLLBACK_INITIATE has happened 231 * persist.sys.rollbacktest.EVENT_TYPE.logPackage: the package to associate the rollback with 232 * persist.sys.rollbacktest.EVENT_TYPE.rollbackReason: the reason Watchdog triggered a rollback 233 * persist.sys.rollbacktest.EVENT_TYPE.failedPackageName: the failing package or process which 234 * triggered the rollback 235 */ logTestProperties(@ullable VersionedPackage logPackage, int type, int rollbackReason, @NonNull String failingPackageName)236 private static void logTestProperties(@Nullable VersionedPackage logPackage, int type, 237 int rollbackReason, @NonNull String failingPackageName) { 238 // This property should be on only during the tests 239 final String prefix = "persist.sys.rollbacktest."; 240 if (!SystemProperties.getBoolean(prefix + "enabled", false)) { 241 return; 242 } 243 String key = prefix + rollbackTypeToString(type); 244 SystemProperties.set(key, String.valueOf(true)); 245 SystemProperties.set(key + ".logPackage", logPackage != null ? logPackage.toString() : ""); 246 SystemProperties.set(key + ".rollbackReason", rollbackReasonToString(rollbackReason)); 247 SystemProperties.set(key + ".failedPackageName", failingPackageName); 248 } 249 250 @VisibleForTesting mapFailureReasonToMetric(@ackageWatchdog.FailureReasons int failureReason)251 static int mapFailureReasonToMetric(@PackageWatchdog.FailureReasons int failureReason) { 252 switch (failureReason) { 253 case PackageWatchdog.FAILURE_REASON_NATIVE_CRASH: 254 return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH; 255 case PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK: 256 return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK; 257 case PackageWatchdog.FAILURE_REASON_APP_CRASH: 258 return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; 259 case PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING: 260 return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; 261 default: 262 return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN; 263 } 264 } 265 rollbackTypeToString(int type)266 private static String rollbackTypeToString(int type) { 267 switch (type) { 268 case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE: 269 return "ROLLBACK_INITIATE"; 270 case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS: 271 return "ROLLBACK_SUCCESS"; 272 case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE: 273 return "ROLLBACK_FAILURE"; 274 case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED: 275 return "ROLLBACK_BOOT_TRIGGERED"; 276 default: 277 return "UNKNOWN"; 278 } 279 } 280 rollbackReasonToString(int reason)281 private static String rollbackReasonToString(int reason) { 282 switch (reason) { 283 case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH: 284 return "REASON_NATIVE_CRASH"; 285 case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK: 286 return "REASON_EXPLICIT_HEALTH_CHECK"; 287 case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH: 288 return "REASON_APP_CRASH"; 289 case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING: 290 return "REASON_APP_NOT_RESPONDING"; 291 case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT: 292 return "REASON_NATIVE_CRASH_DURING_BOOT"; 293 default: 294 return "UNKNOWN"; 295 } 296 } 297 } 298