Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 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; |
| 18 | |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 19 | import static java.lang.annotation.RetentionPolicy.SOURCE; |
| 20 | |
| 21 | import android.annotation.IntDef; |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 22 | import android.annotation.Nullable; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 23 | import android.content.Context; |
Zimuzo | ef65fb8 | 2019-02-28 10:44:29 +0000 | [diff] [blame] | 24 | import android.content.pm.PackageManager; |
Zimuzo | 972e1cd | 2019-01-28 16:30:01 +0000 | [diff] [blame] | 25 | import android.content.pm.VersionedPackage; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 26 | import android.os.Environment; |
| 27 | import android.os.Handler; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 28 | import android.os.Looper; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 29 | import android.os.SystemClock; |
| 30 | import android.text.TextUtils; |
| 31 | import android.util.ArrayMap; |
| 32 | import android.util.AtomicFile; |
| 33 | import android.util.Log; |
| 34 | import android.util.Slog; |
| 35 | import android.util.Xml; |
| 36 | |
| 37 | import com.android.internal.annotations.GuardedBy; |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 38 | import com.android.internal.annotations.VisibleForTesting; |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 39 | import com.android.internal.os.BackgroundThread; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 40 | import com.android.internal.util.FastXmlSerializer; |
| 41 | import com.android.internal.util.XmlUtils; |
| 42 | |
| 43 | import libcore.io.IoUtils; |
| 44 | |
| 45 | import org.xmlpull.v1.XmlPullParser; |
| 46 | import org.xmlpull.v1.XmlPullParserException; |
| 47 | import org.xmlpull.v1.XmlSerializer; |
| 48 | |
| 49 | import java.io.File; |
| 50 | import java.io.FileNotFoundException; |
| 51 | import java.io.FileOutputStream; |
| 52 | import java.io.IOException; |
| 53 | import java.io.InputStream; |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 54 | import java.lang.annotation.Retention; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 55 | import java.nio.charset.StandardCharsets; |
| 56 | import java.util.ArrayList; |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 57 | import java.util.Collections; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 58 | import java.util.Iterator; |
| 59 | import java.util.List; |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 60 | import java.util.Set; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 61 | |
| 62 | /** |
| 63 | * Monitors the health of packages on the system and notifies interested observers when packages |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 64 | * fail. On failure, the registered observer with the least user impacting mitigation will |
| 65 | * be notified. |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 66 | */ |
| 67 | public class PackageWatchdog { |
| 68 | private static final String TAG = "PackageWatchdog"; |
| 69 | // Duration to count package failures before it resets to 0 |
| 70 | private static final int TRIGGER_DURATION_MS = 60000; |
| 71 | // Number of package failures within the duration above before we notify observers |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 72 | static final int TRIGGER_FAILURE_COUNT = 5; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 73 | private static final int DB_VERSION = 1; |
| 74 | private static final String TAG_PACKAGE_WATCHDOG = "package-watchdog"; |
| 75 | private static final String TAG_PACKAGE = "package"; |
| 76 | private static final String TAG_OBSERVER = "observer"; |
| 77 | private static final String ATTR_VERSION = "version"; |
| 78 | private static final String ATTR_NAME = "name"; |
| 79 | private static final String ATTR_DURATION = "duration"; |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 80 | private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check"; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 81 | |
| 82 | private static PackageWatchdog sPackageWatchdog; |
| 83 | |
| 84 | private final Object mLock = new Object(); |
| 85 | // System server context |
| 86 | private final Context mContext; |
| 87 | // Handler to run package cleanup runnables |
| 88 | private final Handler mTimerHandler; |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 89 | // Handler for processing IO and observer actions |
| 90 | private final Handler mWorkerHandler; |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 91 | // Contains (observer-name -> observer-handle) that have ever been registered from |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 92 | // previous boots. Observers with all packages expired are periodically pruned. |
| 93 | // It is saved to disk on system shutdown and repouplated on startup so it survives reboots. |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 94 | @GuardedBy("mLock") |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 95 | private final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>(); |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 96 | // File containing the XML data of monitored packages /data/system/package-watchdog.xml |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 97 | private final AtomicFile mPolicyFile; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 98 | // Runnable to prune monitored packages that have expired |
| 99 | private final Runnable mPackageCleanup; |
| 100 | // Last SystemClock#uptimeMillis a package clean up was executed. |
| 101 | // 0 if mPackageCleanup not running. |
| 102 | private long mUptimeAtLastRescheduleMs; |
| 103 | // Duration a package cleanup was last scheduled for. |
| 104 | // 0 if mPackageCleanup not running. |
| 105 | private long mDurationAtLastReschedule; |
| 106 | |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 107 | // TODO(b/120598832): Remove redundant context param |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 108 | private PackageWatchdog(Context context) { |
| 109 | mContext = context; |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 110 | mPolicyFile = new AtomicFile(new File(new File(Environment.getDataDirectory(), "system"), |
| 111 | "package-watchdog.xml")); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 112 | mTimerHandler = new Handler(Looper.myLooper()); |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 113 | mWorkerHandler = BackgroundThread.getHandler(); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 114 | mPackageCleanup = this::rescheduleCleanup; |
| 115 | loadFromFile(); |
| 116 | } |
| 117 | |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 118 | /** |
| 119 | * Creates a PackageWatchdog for testing that uses the same {@code looper} for all handlers |
| 120 | * and creates package-watchdog.xml in an apps data directory. |
| 121 | */ |
| 122 | @VisibleForTesting |
| 123 | PackageWatchdog(Context context, Looper looper) { |
| 124 | mContext = context; |
| 125 | mPolicyFile = new AtomicFile(new File(context.getFilesDir(), "package-watchdog.xml")); |
| 126 | mTimerHandler = new Handler(looper); |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 127 | mWorkerHandler = mTimerHandler; |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 128 | mPackageCleanup = this::rescheduleCleanup; |
| 129 | loadFromFile(); |
| 130 | } |
| 131 | |
| 132 | |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 133 | /** Creates or gets singleton instance of PackageWatchdog. */ |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 134 | public static PackageWatchdog getInstance(Context context) { |
| 135 | synchronized (PackageWatchdog.class) { |
| 136 | if (sPackageWatchdog == null) { |
| 137 | sPackageWatchdog = new PackageWatchdog(context); |
| 138 | } |
| 139 | return sPackageWatchdog; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 140 | } |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 141 | } |
| 142 | |
| 143 | /** |
| 144 | * Registers {@code observer} to listen for package failures |
| 145 | * |
| 146 | * <p>Observers are expected to call this on boot. It does not specify any packages but |
| 147 | * it will resume observing any packages requested from a previous boot. |
| 148 | */ |
| 149 | public void registerHealthObserver(PackageHealthObserver observer) { |
| 150 | synchronized (mLock) { |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 151 | ObserverInternal internalObserver = mAllObservers.get(observer.getName()); |
| 152 | if (internalObserver != null) { |
| 153 | internalObserver.mRegisteredObserver = observer; |
| 154 | } |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 155 | if (mDurationAtLastReschedule == 0) { |
| 156 | // Nothing running, schedule |
| 157 | rescheduleCleanup(); |
| 158 | } |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | /** |
| 163 | * Starts observing the health of the {@code packages} for {@code observer} and notifies |
| 164 | * {@code observer} of any package failures within the monitoring duration. |
| 165 | * |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 166 | * <p>If monitoring a package with {@code withExplicitHealthCheck}, at the end of the monitoring |
| 167 | * duration if {@link #onExplicitHealthCheckPassed} was never called, |
| 168 | * {@link PackageHealthObserver#execute} will be called as if the package failed. |
| 169 | * |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 170 | * <p>If {@code observer} is already monitoring a package in {@code packageNames}, |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 171 | * the monitoring window of that package will be reset to {@code durationMs} and the health |
| 172 | * check state will be reset to a default depending on {@code withExplictHealthCheck}. |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 173 | * |
| 174 | * @throws IllegalArgumentException if {@code packageNames} is empty |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 175 | * or {@code durationMs} is less than 1 |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 176 | */ |
| 177 | public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames, |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 178 | long durationMs, boolean withExplicitHealthCheck) { |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 179 | if (packageNames.isEmpty() || durationMs < 1) { |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 180 | throw new IllegalArgumentException("Observation not started, no packages specified" |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 181 | + "or invalid duration"); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 182 | } |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 183 | List<MonitoredPackage> packages = new ArrayList<>(); |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 184 | for (int i = 0; i < packageNames.size(); i++) { |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 185 | // When observing packages withExplicitHealthCheck, |
| 186 | // MonitoredPackage#mHasExplicitHealthCheckPassed will be false initially. |
| 187 | packages.add(new MonitoredPackage(packageNames.get(i), durationMs, |
| 188 | !withExplicitHealthCheck)); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 189 | } |
| 190 | synchronized (mLock) { |
| 191 | ObserverInternal oldObserver = mAllObservers.get(observer.getName()); |
| 192 | if (oldObserver == null) { |
| 193 | Slog.d(TAG, observer.getName() + " started monitoring health of packages " |
| 194 | + packageNames); |
| 195 | mAllObservers.put(observer.getName(), |
| 196 | new ObserverInternal(observer.getName(), packages)); |
| 197 | } else { |
| 198 | Slog.d(TAG, observer.getName() + " added the following packages to monitor " |
| 199 | + packageNames); |
| 200 | oldObserver.updatePackages(packages); |
| 201 | } |
| 202 | } |
| 203 | registerHealthObserver(observer); |
| 204 | // Always reschedule because we may need to expire packages |
| 205 | // earlier than we are already scheduled for |
| 206 | rescheduleCleanup(); |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 207 | saveToFileAsync(); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 208 | } |
| 209 | |
| 210 | /** |
| 211 | * Unregisters {@code observer} from listening to package failure. |
| 212 | * Additionally, this stops observing any packages that may have previously been observed |
| 213 | * even from a previous boot. |
| 214 | */ |
| 215 | public void unregisterHealthObserver(PackageHealthObserver observer) { |
| 216 | synchronized (mLock) { |
| 217 | mAllObservers.remove(observer.getName()); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 218 | } |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 219 | saveToFileAsync(); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 220 | } |
| 221 | |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 222 | /** |
| 223 | * Returns packages observed by {@code observer} |
| 224 | * |
| 225 | * @return an empty set if {@code observer} has some packages observerd from a previous boot |
| 226 | * but has not registered itself in the current boot to receive notifications. Returns null |
| 227 | * if there are no active packages monitored from any boot. |
| 228 | */ |
| 229 | @Nullable |
| 230 | public Set<String> getPackages(PackageHealthObserver observer) { |
| 231 | synchronized (mLock) { |
| 232 | for (int i = 0; i < mAllObservers.size(); i++) { |
| 233 | if (observer.getName().equals(mAllObservers.keyAt(i))) { |
| 234 | if (observer.equals(mAllObservers.valueAt(i).mRegisteredObserver)) { |
| 235 | return mAllObservers.valueAt(i).mPackages.keySet(); |
| 236 | } |
| 237 | return Collections.emptySet(); |
| 238 | } |
| 239 | } |
| 240 | } |
| 241 | return null; |
| 242 | } |
| 243 | |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 244 | /** |
| 245 | * Called when a process fails either due to a crash or ANR. |
| 246 | * |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 247 | * <p>For each package contained in the process, one registered observer with the least user |
| 248 | * impact will be notified for mitigation. |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 249 | * |
| 250 | * <p>This method could be called frequently if there is a severe problem on the device. |
| 251 | */ |
Zimuzo | 972e1cd | 2019-01-28 16:30:01 +0000 | [diff] [blame] | 252 | public void onPackageFailure(List<VersionedPackage> packages) { |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 253 | mWorkerHandler.post(() -> { |
| 254 | synchronized (mLock) { |
| 255 | if (mAllObservers.isEmpty()) { |
| 256 | return; |
| 257 | } |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 258 | |
Zimuzo | 972e1cd | 2019-01-28 16:30:01 +0000 | [diff] [blame] | 259 | for (int pIndex = 0; pIndex < packages.size(); pIndex++) { |
Zimuzo | 71d931e | 2019-02-01 13:08:16 +0000 | [diff] [blame] | 260 | VersionedPackage versionedPackage = packages.get(pIndex); |
| 261 | // Observer that will receive failure for versionedPackage |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 262 | PackageHealthObserver currentObserverToNotify = null; |
| 263 | int currentObserverImpact = Integer.MAX_VALUE; |
| 264 | |
| 265 | // Find observer with least user impact |
| 266 | for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { |
| 267 | ObserverInternal observer = mAllObservers.valueAt(oIndex); |
| 268 | PackageHealthObserver registeredObserver = observer.mRegisteredObserver; |
| 269 | if (registeredObserver != null |
Zimuzo | 71d931e | 2019-02-01 13:08:16 +0000 | [diff] [blame] | 270 | && observer.onPackageFailure(versionedPackage.getPackageName())) { |
| 271 | int impact = registeredObserver.onHealthCheckFailed(versionedPackage); |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 272 | if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE |
| 273 | && impact < currentObserverImpact) { |
| 274 | currentObserverToNotify = registeredObserver; |
| 275 | currentObserverImpact = impact; |
| 276 | } |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | // Execute action with least user impact |
| 281 | if (currentObserverToNotify != null) { |
Zimuzo | 71d931e | 2019-02-01 13:08:16 +0000 | [diff] [blame] | 282 | currentObserverToNotify.execute(versionedPackage); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 283 | } |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 284 | } |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 285 | } |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 286 | }); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 287 | } |
| 288 | |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 289 | /** |
| 290 | * Updates the observers monitoring {@code packageName} that explicit health check has passed. |
| 291 | * |
| 292 | * <p> This update is strictly for registered observers at the time of the call |
| 293 | * Observers that register after this signal will have no knowledge of prior signals and will |
| 294 | * effectively behave as if the explicit health check hasn't passed for {@code packageName}. |
| 295 | * |
| 296 | * <p> {@code packageName} can still be considered failed if reported by |
| 297 | * {@link #onPackageFailure} before the package expires. |
| 298 | * |
| 299 | * <p> Triggered by components outside the system server when they are fully functional after an |
| 300 | * update. |
| 301 | */ |
| 302 | public void onExplicitHealthCheckPassed(String packageName) { |
| 303 | Slog.i(TAG, "Health check passed for package: " + packageName); |
| 304 | boolean shouldUpdateFile = false; |
| 305 | synchronized (mLock) { |
| 306 | for (int observerIdx = 0; observerIdx < mAllObservers.size(); observerIdx++) { |
| 307 | ObserverInternal observer = mAllObservers.valueAt(observerIdx); |
| 308 | MonitoredPackage monitoredPackage = observer.mPackages.get(packageName); |
| 309 | if (monitoredPackage != null && !monitoredPackage.mHasPassedHealthCheck) { |
| 310 | monitoredPackage.mHasPassedHealthCheck = true; |
| 311 | shouldUpdateFile = true; |
| 312 | } |
| 313 | } |
| 314 | } |
| 315 | if (shouldUpdateFile) { |
| 316 | saveToFileAsync(); |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | // TODO(b/120598832): Optimize write? Maybe only write a separate smaller file? |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 321 | // This currently adds about 7ms extra to shutdown thread |
| 322 | /** Writes the package information to file during shutdown. */ |
| 323 | public void writeNow() { |
| 324 | if (!mAllObservers.isEmpty()) { |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 325 | mWorkerHandler.removeCallbacks(this::saveToFile); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 326 | pruneObservers(SystemClock.uptimeMillis() - mUptimeAtLastRescheduleMs); |
| 327 | saveToFile(); |
| 328 | Slog.i(TAG, "Last write to update package durations"); |
| 329 | } |
| 330 | } |
| 331 | |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 332 | /** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. */ |
| 333 | @Retention(SOURCE) |
| 334 | @IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_NONE, |
| 335 | PackageHealthObserverImpact.USER_IMPACT_LOW, |
| 336 | PackageHealthObserverImpact.USER_IMPACT_MEDIUM, |
| 337 | PackageHealthObserverImpact.USER_IMPACT_HIGH}) |
| 338 | public @interface PackageHealthObserverImpact { |
| 339 | /** No action to take. */ |
| 340 | int USER_IMPACT_NONE = 0; |
| 341 | /* Action has low user impact, user of a device will barely notice. */ |
| 342 | int USER_IMPACT_LOW = 1; |
| 343 | /* Action has medium user impact, user of a device will likely notice. */ |
| 344 | int USER_IMPACT_MEDIUM = 3; |
| 345 | /* Action has high user impact, a last resort, user of a device will be very frustrated. */ |
| 346 | int USER_IMPACT_HIGH = 5; |
| 347 | } |
| 348 | |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 349 | /** Register instances of this interface to receive notifications on package failure. */ |
| 350 | public interface PackageHealthObserver { |
| 351 | /** |
Zimuzo | 71d931e | 2019-02-01 13:08:16 +0000 | [diff] [blame] | 352 | * Called when health check fails for the {@code versionedPackage}. |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 353 | * |
| 354 | * @return any one of {@link PackageHealthObserverImpact} to express the impact |
| 355 | * to the user on {@link #execute} |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 356 | */ |
Zimuzo | 71d931e | 2019-02-01 13:08:16 +0000 | [diff] [blame] | 357 | @PackageHealthObserverImpact int onHealthCheckFailed(VersionedPackage versionedPackage); |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 358 | |
| 359 | /** |
| 360 | * Executes mitigation for {@link #onHealthCheckFailed}. |
| 361 | * |
| 362 | * @return {@code true} if action was executed successfully, {@code false} otherwise |
| 363 | */ |
Zimuzo | 71d931e | 2019-02-01 13:08:16 +0000 | [diff] [blame] | 364 | boolean execute(VersionedPackage versionedPackage); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 365 | |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 366 | // TODO(b/120598832): Ensure uniqueness? |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 367 | /** |
| 368 | * Identifier for the observer, should not change across device updates otherwise the |
| 369 | * watchdog may drop observing packages with the old name. |
| 370 | */ |
| 371 | String getName(); |
| 372 | } |
| 373 | |
| 374 | /** Reschedules handler to prune expired packages from observers. */ |
| 375 | private void rescheduleCleanup() { |
| 376 | synchronized (mLock) { |
| 377 | long nextDurationToScheduleMs = getEarliestPackageExpiryLocked(); |
| 378 | if (nextDurationToScheduleMs == Long.MAX_VALUE) { |
| 379 | Slog.i(TAG, "No monitored packages, ending package cleanup"); |
| 380 | mDurationAtLastReschedule = 0; |
| 381 | mUptimeAtLastRescheduleMs = 0; |
| 382 | return; |
| 383 | } |
| 384 | long uptimeMs = SystemClock.uptimeMillis(); |
| 385 | // O if mPackageCleanup not running |
| 386 | long elapsedDurationMs = mUptimeAtLastRescheduleMs == 0 |
| 387 | ? 0 : uptimeMs - mUptimeAtLastRescheduleMs; |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 388 | // Less than O if mPackageCleanup unexpectedly didn't run yet even though |
| 389 | // and we are past the last duration scheduled to run |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 390 | long remainingDurationMs = mDurationAtLastReschedule - elapsedDurationMs; |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 391 | if (mUptimeAtLastRescheduleMs == 0 |
| 392 | || remainingDurationMs <= 0 |
| 393 | || nextDurationToScheduleMs < remainingDurationMs) { |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 394 | // First schedule or an earlier reschedule |
| 395 | pruneObservers(elapsedDurationMs); |
| 396 | mTimerHandler.removeCallbacks(mPackageCleanup); |
| 397 | mTimerHandler.postDelayed(mPackageCleanup, nextDurationToScheduleMs); |
| 398 | mDurationAtLastReschedule = nextDurationToScheduleMs; |
| 399 | mUptimeAtLastRescheduleMs = uptimeMs; |
| 400 | } |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | /** |
| 405 | * Returns the earliest time a package should expire. |
| 406 | * @returns Long#MAX_VALUE if there are no observed packages. |
| 407 | */ |
| 408 | private long getEarliestPackageExpiryLocked() { |
| 409 | long shortestDurationMs = Long.MAX_VALUE; |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 410 | for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { |
| 411 | ArrayMap<String, MonitoredPackage> packages = mAllObservers.valueAt(oIndex).mPackages; |
| 412 | for (int pIndex = 0; pIndex < packages.size(); pIndex++) { |
| 413 | long duration = packages.valueAt(pIndex).mDurationMs; |
| 414 | if (duration < shortestDurationMs) { |
| 415 | shortestDurationMs = duration; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 416 | } |
| 417 | } |
| 418 | } |
| 419 | Slog.v(TAG, "Earliest package time is " + shortestDurationMs); |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 420 | |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 421 | return shortestDurationMs; |
| 422 | } |
| 423 | |
| 424 | /** |
| 425 | * Removes {@code elapsedMs} milliseconds from all durations on monitored packages. |
| 426 | * Discards expired packages and discards observers without any packages. |
| 427 | */ |
| 428 | private void pruneObservers(long elapsedMs) { |
| 429 | if (elapsedMs == 0) { |
| 430 | return; |
| 431 | } |
| 432 | synchronized (mLock) { |
| 433 | Slog.d(TAG, "Removing expired packages after " + elapsedMs + "ms"); |
| 434 | Iterator<ObserverInternal> it = mAllObservers.values().iterator(); |
| 435 | while (it.hasNext()) { |
| 436 | ObserverInternal observer = it.next(); |
Zimuzo | ef65fb8 | 2019-02-28 10:44:29 +0000 | [diff] [blame] | 437 | List<MonitoredPackage> failedPackages = |
| 438 | observer.updateMonitoringDurations(elapsedMs); |
| 439 | if (!failedPackages.isEmpty()) { |
| 440 | onExplicitHealthCheckFailed(observer, failedPackages); |
| 441 | } |
| 442 | if (observer.mPackages.isEmpty()) { |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 443 | Slog.i(TAG, "Discarding observer " + observer.mName + ". All packages expired"); |
| 444 | it.remove(); |
| 445 | } |
| 446 | } |
| 447 | } |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 448 | saveToFileAsync(); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 449 | } |
| 450 | |
Zimuzo | ef65fb8 | 2019-02-28 10:44:29 +0000 | [diff] [blame] | 451 | private void onExplicitHealthCheckFailed(ObserverInternal observer, |
| 452 | List<MonitoredPackage> failedPackages) { |
| 453 | mWorkerHandler.post(() -> { |
| 454 | synchronized (mLock) { |
| 455 | PackageHealthObserver registeredObserver = observer.mRegisteredObserver; |
| 456 | if (registeredObserver != null) { |
| 457 | PackageManager pm = mContext.getPackageManager(); |
| 458 | for (int i = 0; i < failedPackages.size(); i++) { |
| 459 | String packageName = failedPackages.get(i).mName; |
| 460 | long versionCode = 0; |
| 461 | try { |
| 462 | versionCode = pm.getPackageInfo( |
| 463 | packageName, 0 /* flags */).getLongVersionCode(); |
| 464 | } catch (PackageManager.NameNotFoundException e) { |
| 465 | Slog.w(TAG, "Explicit health check failed but could not find package " |
| 466 | + packageName); |
| 467 | // TODO(b/120598832): Skip. We only continue to pass tests for now since |
| 468 | // the tests don't install any packages |
| 469 | } |
| 470 | registeredObserver.execute(new VersionedPackage(packageName, versionCode)); |
| 471 | } |
| 472 | } |
| 473 | } |
| 474 | }); |
| 475 | } |
| 476 | |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 477 | /** |
| 478 | * Loads mAllObservers from file. |
| 479 | * |
| 480 | * <p>Note that this is <b>not</b> thread safe and should only called be called |
| 481 | * from the constructor. |
| 482 | */ |
| 483 | private void loadFromFile() { |
| 484 | InputStream infile = null; |
| 485 | mAllObservers.clear(); |
| 486 | try { |
| 487 | infile = mPolicyFile.openRead(); |
| 488 | final XmlPullParser parser = Xml.newPullParser(); |
| 489 | parser.setInput(infile, StandardCharsets.UTF_8.name()); |
| 490 | XmlUtils.beginDocument(parser, TAG_PACKAGE_WATCHDOG); |
| 491 | int outerDepth = parser.getDepth(); |
| 492 | while (XmlUtils.nextElementWithin(parser, outerDepth)) { |
| 493 | ObserverInternal observer = ObserverInternal.read(parser); |
| 494 | if (observer != null) { |
| 495 | mAllObservers.put(observer.mName, observer); |
| 496 | } |
| 497 | } |
| 498 | } catch (FileNotFoundException e) { |
| 499 | // Nothing to monitor |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 500 | } catch (IOException | NumberFormatException | XmlPullParserException e) { |
| 501 | Log.wtf(TAG, "Unable to read monitored packages, deleting file", e); |
| 502 | mPolicyFile.delete(); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 503 | } finally { |
| 504 | IoUtils.closeQuietly(infile); |
| 505 | } |
| 506 | } |
| 507 | |
| 508 | /** |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 509 | * Persists mAllObservers to file. Threshold information is ignored. |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 510 | */ |
| 511 | private boolean saveToFile() { |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 512 | synchronized (mLock) { |
| 513 | FileOutputStream stream; |
| 514 | try { |
| 515 | stream = mPolicyFile.startWrite(); |
| 516 | } catch (IOException e) { |
| 517 | Slog.w(TAG, "Cannot update monitored packages", e); |
| 518 | return false; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 519 | } |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 520 | |
| 521 | try { |
| 522 | XmlSerializer out = new FastXmlSerializer(); |
| 523 | out.setOutput(stream, StandardCharsets.UTF_8.name()); |
| 524 | out.startDocument(null, true); |
| 525 | out.startTag(null, TAG_PACKAGE_WATCHDOG); |
| 526 | out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); |
| 527 | for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { |
| 528 | mAllObservers.valueAt(oIndex).write(out); |
| 529 | } |
| 530 | out.endTag(null, TAG_PACKAGE_WATCHDOG); |
| 531 | out.endDocument(); |
| 532 | mPolicyFile.finishWrite(stream); |
| 533 | return true; |
| 534 | } catch (IOException e) { |
| 535 | Slog.w(TAG, "Failed to save monitored packages, restoring backup", e); |
| 536 | mPolicyFile.failWrite(stream); |
| 537 | return false; |
| 538 | } finally { |
| 539 | IoUtils.closeQuietly(stream); |
| 540 | } |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 541 | } |
| 542 | } |
| 543 | |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 544 | private void saveToFileAsync() { |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 545 | mWorkerHandler.removeCallbacks(this::saveToFile); |
| 546 | mWorkerHandler.post(this::saveToFile); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 547 | } |
| 548 | |
| 549 | /** |
| 550 | * Represents an observer monitoring a set of packages along with the failure thresholds for |
| 551 | * each package. |
| 552 | */ |
| 553 | static class ObserverInternal { |
| 554 | public final String mName; |
Zimuzo | ef65fb8 | 2019-02-28 10:44:29 +0000 | [diff] [blame] | 555 | //TODO(b/120598832): Add getter for mPackages |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 556 | public final ArrayMap<String, MonitoredPackage> mPackages; |
Zimuzo | 3eee438 | 2019-01-08 20:42:39 +0000 | [diff] [blame] | 557 | @Nullable |
| 558 | public PackageHealthObserver mRegisteredObserver; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 559 | |
| 560 | ObserverInternal(String name, List<MonitoredPackage> packages) { |
| 561 | mName = name; |
| 562 | mPackages = new ArrayMap<>(); |
| 563 | updatePackages(packages); |
| 564 | } |
| 565 | |
| 566 | /** |
| 567 | * Writes important details to file. Doesn't persist any package failure thresholds. |
| 568 | * |
| 569 | * <p>Note that this method is <b>not</b> thread safe. It should only be called from |
| 570 | * #saveToFile which runs on a single threaded handler. |
| 571 | */ |
| 572 | public boolean write(XmlSerializer out) { |
| 573 | try { |
| 574 | out.startTag(null, TAG_OBSERVER); |
| 575 | out.attribute(null, ATTR_NAME, mName); |
| 576 | for (int i = 0; i < mPackages.size(); i++) { |
| 577 | MonitoredPackage p = mPackages.valueAt(i); |
| 578 | out.startTag(null, TAG_PACKAGE); |
| 579 | out.attribute(null, ATTR_NAME, p.mName); |
| 580 | out.attribute(null, ATTR_DURATION, String.valueOf(p.mDurationMs)); |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 581 | out.attribute(null, ATTR_PASSED_HEALTH_CHECK, |
| 582 | String.valueOf(p.mHasPassedHealthCheck)); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 583 | out.endTag(null, TAG_PACKAGE); |
| 584 | } |
| 585 | out.endTag(null, TAG_OBSERVER); |
| 586 | return true; |
| 587 | } catch (IOException e) { |
| 588 | Slog.w(TAG, "Cannot save observer", e); |
| 589 | return false; |
| 590 | } |
| 591 | } |
| 592 | |
| 593 | public void updatePackages(List<MonitoredPackage> packages) { |
| 594 | synchronized (mName) { |
Zimuzo | cfaed76 | 2019-01-03 21:13:01 +0000 | [diff] [blame] | 595 | for (int pIndex = 0; pIndex < packages.size(); pIndex++) { |
| 596 | MonitoredPackage p = packages.get(pIndex); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 597 | mPackages.put(p.mName, p); |
| 598 | } |
| 599 | } |
| 600 | } |
| 601 | |
| 602 | /** |
| 603 | * Reduces the monitoring durations of all packages observed by this observer by |
| 604 | * {@code elapsedMs}. If any duration is less than 0, the package is removed from |
| 605 | * observation. |
| 606 | * |
Zimuzo | ef65fb8 | 2019-02-28 10:44:29 +0000 | [diff] [blame] | 607 | * @returns a {@link List} of packages that were removed from the observer without explicit |
| 608 | * health check passing, or an empty list if no package expired for which an explicit health |
| 609 | * check was still pending |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 610 | */ |
Zimuzo | ef65fb8 | 2019-02-28 10:44:29 +0000 | [diff] [blame] | 611 | public List<MonitoredPackage> updateMonitoringDurations(long elapsedMs) { |
| 612 | List<MonitoredPackage> removedPackages = new ArrayList<>(); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 613 | synchronized (mName) { |
| 614 | Iterator<MonitoredPackage> it = mPackages.values().iterator(); |
| 615 | while (it.hasNext()) { |
| 616 | MonitoredPackage p = it.next(); |
| 617 | long newDuration = p.mDurationMs - elapsedMs; |
| 618 | if (newDuration > 0) { |
| 619 | p.mDurationMs = newDuration; |
| 620 | } else { |
Zimuzo | ef65fb8 | 2019-02-28 10:44:29 +0000 | [diff] [blame] | 621 | if (!p.mHasPassedHealthCheck) { |
| 622 | removedPackages.add(p); |
| 623 | } |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 624 | it.remove(); |
| 625 | } |
| 626 | } |
Zimuzo | ef65fb8 | 2019-02-28 10:44:29 +0000 | [diff] [blame] | 627 | return removedPackages; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 628 | } |
| 629 | } |
| 630 | |
| 631 | /** |
| 632 | * Increments failure counts of {@code packageName}. |
| 633 | * @returns {@code true} if failure threshold is exceeded, {@code false} otherwise |
| 634 | */ |
| 635 | public boolean onPackageFailure(String packageName) { |
| 636 | synchronized (mName) { |
| 637 | MonitoredPackage p = mPackages.get(packageName); |
| 638 | if (p != null) { |
| 639 | return p.onFailure(); |
| 640 | } |
| 641 | return false; |
| 642 | } |
| 643 | } |
| 644 | |
| 645 | /** |
| 646 | * Returns one ObserverInternal from the {@code parser} and advances its state. |
| 647 | * |
| 648 | * <p>Note that this method is <b>not</b> thread safe. It should only be called from |
| 649 | * #loadFromFile which in turn is only called on construction of the |
| 650 | * singleton PackageWatchdog. |
| 651 | **/ |
| 652 | public static ObserverInternal read(XmlPullParser parser) { |
| 653 | String observerName = null; |
| 654 | if (TAG_OBSERVER.equals(parser.getName())) { |
| 655 | observerName = parser.getAttributeValue(null, ATTR_NAME); |
| 656 | if (TextUtils.isEmpty(observerName)) { |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 657 | Slog.wtf(TAG, "Unable to read observer name"); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 658 | return null; |
| 659 | } |
| 660 | } |
| 661 | List<MonitoredPackage> packages = new ArrayList<>(); |
| 662 | int innerDepth = parser.getDepth(); |
| 663 | try { |
| 664 | while (XmlUtils.nextElementWithin(parser, innerDepth)) { |
| 665 | if (TAG_PACKAGE.equals(parser.getName())) { |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 666 | try { |
| 667 | String packageName = parser.getAttributeValue(null, ATTR_NAME); |
| 668 | long duration = Long.parseLong( |
| 669 | parser.getAttributeValue(null, ATTR_DURATION)); |
| 670 | boolean hasPassedHealthCheck = Boolean.parseBoolean( |
| 671 | parser.getAttributeValue(null, ATTR_PASSED_HEALTH_CHECK)); |
| 672 | if (!TextUtils.isEmpty(packageName)) { |
| 673 | packages.add(new MonitoredPackage(packageName, duration, |
| 674 | hasPassedHealthCheck)); |
| 675 | } |
| 676 | } catch (NumberFormatException e) { |
| 677 | Slog.wtf(TAG, "Skipping package for observer " + observerName, e); |
| 678 | continue; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 679 | } |
| 680 | } |
| 681 | } |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 682 | } catch (XmlPullParserException | IOException e) { |
| 683 | Slog.wtf(TAG, "Unable to read observer " + observerName, e); |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 684 | return null; |
| 685 | } |
| 686 | if (packages.isEmpty()) { |
| 687 | return null; |
| 688 | } |
| 689 | return new ObserverInternal(observerName, packages); |
| 690 | } |
| 691 | } |
| 692 | |
| 693 | /** Represents a package along with the time it should be monitored for. */ |
| 694 | static class MonitoredPackage { |
| 695 | public final String mName; |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 696 | // Whether an explicit health check has passed |
| 697 | public boolean mHasPassedHealthCheck; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 698 | // System uptime duration to monitor package |
| 699 | public long mDurationMs; |
| 700 | // System uptime of first package failure |
| 701 | private long mUptimeStartMs; |
| 702 | // Number of failures since mUptimeStartMs |
| 703 | private int mFailures; |
| 704 | |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 705 | MonitoredPackage(String name, long durationMs, boolean hasPassedHealthCheck) { |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 706 | mName = name; |
| 707 | mDurationMs = durationMs; |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 708 | mHasPassedHealthCheck = hasPassedHealthCheck; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 709 | } |
| 710 | |
| 711 | /** |
| 712 | * Increment package failures or resets failure count depending on the last package failure. |
| 713 | * |
| 714 | * @return {@code true} if failure count exceeds a threshold, {@code false} otherwise |
| 715 | */ |
| 716 | public synchronized boolean onFailure() { |
| 717 | final long now = SystemClock.uptimeMillis(); |
| 718 | final long duration = now - mUptimeStartMs; |
| 719 | if (duration > TRIGGER_DURATION_MS) { |
Zimuzo | 9284e74 | 2019-02-22 12:09:28 +0000 | [diff] [blame] | 720 | // TODO(b/120598832): Reseting to 1 is not correct |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 721 | // because there may be more than 1 failure in the last trigger window from now |
| 722 | // This is the RescueParty impl, will leave for now |
| 723 | mFailures = 1; |
| 724 | mUptimeStartMs = now; |
| 725 | } else { |
| 726 | mFailures++; |
| 727 | } |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 728 | boolean failed = mFailures >= TRIGGER_FAILURE_COUNT; |
| 729 | if (failed) { |
| 730 | mFailures = 0; |
| 731 | } |
| 732 | return failed; |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 733 | } |
| 734 | } |
Zimuzo | 6efba54 | 2018-11-29 12:47:58 +0000 | [diff] [blame] | 735 | } |