• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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