Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 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 | |
Gavin Corkery | dd1daba | 2019-11-27 19:11:13 +0000 | [diff] [blame] | 19 | import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; |
| 20 | import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; |
| 21 | import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK; |
| 22 | import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH; |
| 23 | import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN; |
| 24 | |
| 25 | import android.annotation.NonNull; |
Zimuzo | da97236 | 2019-04-27 19:57:58 +0100 | [diff] [blame] | 26 | import android.annotation.Nullable; |
Zimuzo | ff99176 | 2019-02-01 19:25:44 +0000 | [diff] [blame] | 27 | import android.content.BroadcastReceiver; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 28 | import android.content.Context; |
| 29 | import android.content.Intent; |
Zimuzo | ff99176 | 2019-02-01 19:25:44 +0000 | [diff] [blame] | 30 | import android.content.IntentFilter; |
| 31 | import android.content.pm.PackageInstaller; |
Zimuzo | 03eeb13 | 2019-01-30 15:13:56 +0000 | [diff] [blame] | 32 | import android.content.pm.PackageManager; |
Zimuzo | c119780 | 2019-01-30 12:05:41 +0000 | [diff] [blame] | 33 | import android.content.pm.VersionedPackage; |
Richard Uhler | 0e96192 | 2019-01-25 13:07:08 +0000 | [diff] [blame] | 34 | import android.content.rollback.PackageRollbackInfo; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 35 | import android.content.rollback.RollbackInfo; |
| 36 | import android.content.rollback.RollbackManager; |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 37 | import android.os.Environment; |
| 38 | import android.os.FileUtils; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 39 | import android.os.Handler; |
| 40 | import android.os.HandlerThread; |
Zimuzo | ff99176 | 2019-02-01 19:25:44 +0000 | [diff] [blame] | 41 | import android.os.PowerManager; |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 42 | import android.os.SystemProperties; |
Zimuzo | 03eeb13 | 2019-01-30 15:13:56 +0000 | [diff] [blame] | 43 | import android.text.TextUtils; |
Zimuzo | 8d231b4 | 2019-05-16 18:09:23 +0100 | [diff] [blame] | 44 | import android.util.ArraySet; |
Zimuzo | 03eeb13 | 2019-01-30 15:13:56 +0000 | [diff] [blame] | 45 | import android.util.Slog; |
| 46 | import android.util.StatsLog; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 47 | |
Zimuzo | 03eeb13 | 2019-01-30 15:13:56 +0000 | [diff] [blame] | 48 | import com.android.internal.R; |
Zimuzo | 8d231b4 | 2019-05-16 18:09:23 +0100 | [diff] [blame] | 49 | import com.android.internal.annotations.GuardedBy; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 50 | import com.android.server.PackageWatchdog; |
Gavin Corkery | f305f4d | 2019-11-27 15:46:29 +0000 | [diff] [blame] | 51 | import com.android.server.PackageWatchdog.FailureReasons; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 52 | import com.android.server.PackageWatchdog.PackageHealthObserver; |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 53 | import com.android.server.PackageWatchdog.PackageHealthObserverImpact; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 54 | |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 55 | import libcore.io.IoUtils; |
| 56 | |
| 57 | import java.io.File; |
| 58 | import java.io.FileOutputStream; |
| 59 | import java.io.IOException; |
| 60 | import java.io.PrintWriter; |
Richard Uhler | bf5b5c4 | 2019-01-28 15:26:37 +0000 | [diff] [blame] | 61 | import java.util.Collections; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 62 | import java.util.List; |
Zimuzo | 8d231b4 | 2019-05-16 18:09:23 +0100 | [diff] [blame] | 63 | import java.util.Set; |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 64 | import java.util.concurrent.TimeUnit; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 65 | |
| 66 | /** |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 67 | * {@link PackageHealthObserver} for {@link RollbackManagerService}. |
| 68 | * This class monitors crashes and triggers RollbackManager rollback accordingly. |
| 69 | * It also monitors native crashes for some short while after boot. |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 70 | * |
| 71 | * @hide |
| 72 | */ |
| 73 | public final class RollbackPackageHealthObserver implements PackageHealthObserver { |
| 74 | private static final String TAG = "RollbackPackageHealthObserver"; |
| 75 | private static final String NAME = "rollback-observer"; |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 76 | private static final int INVALID_ROLLBACK_ID = -1; |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 77 | // TODO: make the following values configurable via DeviceConfig |
| 78 | private static final long NATIVE_CRASH_POLLING_INTERVAL_MILLIS = |
| 79 | TimeUnit.SECONDS.toMillis(30); |
| 80 | private static final long NUMBER_OF_NATIVE_CRASH_POLLS = 10; |
| 81 | |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 82 | private final Context mContext; |
| 83 | private final Handler mHandler; |
| 84 | private final File mLastStagedRollbackIdFile; |
Zimuzo | 8d231b4 | 2019-05-16 18:09:23 +0100 | [diff] [blame] | 85 | // Staged rollback ids that have been committed but their session is not yet ready |
| 86 | @GuardedBy("mPendingStagedRollbackIds") |
| 87 | private final Set<Integer> mPendingStagedRollbackIds = new ArraySet<>(); |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 88 | // this field is initialized in the c'tor and then only accessed from mHandler thread, so |
| 89 | // no need to guard with a lock |
| 90 | private long mNumberOfNativeCrashPollsRemaining; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 91 | |
| 92 | RollbackPackageHealthObserver(Context context) { |
| 93 | mContext = context; |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 94 | mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 95 | HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver"); |
| 96 | handlerThread.start(); |
| 97 | mHandler = handlerThread.getThreadHandler(); |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 98 | File dataDir = new File(Environment.getDataDirectory(), "rollback-observer"); |
| 99 | dataDir.mkdirs(); |
| 100 | mLastStagedRollbackIdFile = new File(dataDir, "last-staged-rollback-id"); |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 101 | PackageWatchdog.getInstance(mContext).registerHealthObserver(this); |
| 102 | } |
| 103 | |
| 104 | @Override |
Zimuzo | 71d931e | 2019-02-01 13:08:16 +0000 | [diff] [blame] | 105 | public int onHealthCheckFailed(VersionedPackage failedPackage) { |
Mohammad Samiul Islam | 7bd8788 | 2019-11-21 17:16:41 +0000 | [diff] [blame] | 106 | if (getAvailableRollback(failedPackage) == null) { |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 107 | // Don't handle the notification, no rollbacks available for the package |
| 108 | return PackageHealthObserverImpact.USER_IMPACT_NONE; |
Zimuzo | da97236 | 2019-04-27 19:57:58 +0100 | [diff] [blame] | 109 | } else { |
| 110 | // Rollback is available, we may get a callback into #execute |
| 111 | return PackageHealthObserverImpact.USER_IMPACT_MEDIUM; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 112 | } |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 113 | } |
| 114 | |
| 115 | @Override |
Gavin Corkery | f305f4d | 2019-11-27 15:46:29 +0000 | [diff] [blame] | 116 | public boolean execute(VersionedPackage failedPackage, @FailureReasons int rollbackReason) { |
Mohammad Samiul Islam | 7bd8788 | 2019-11-21 17:16:41 +0000 | [diff] [blame] | 117 | RollbackInfo rollback = getAvailableRollback(failedPackage); |
Zimuzo | da97236 | 2019-04-27 19:57:58 +0100 | [diff] [blame] | 118 | if (rollback == null) { |
Zimuzo | 9e57ecb | 2019-02-04 15:34:08 +0000 | [diff] [blame] | 119 | Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ " |
Zimuzo | 71d931e | 2019-02-01 13:08:16 +0000 | [diff] [blame] | 120 | + failedPackage.getPackageName() + "] with versionCode: [" |
| 121 | + failedPackage.getVersionCode() + "]"); |
Zimuzo | 03eeb13 | 2019-01-30 15:13:56 +0000 | [diff] [blame] | 122 | return false; |
| 123 | } |
Mohammad Samiul Islam | 7bd8788 | 2019-11-21 17:16:41 +0000 | [diff] [blame] | 124 | rollbackPackage(rollback, failedPackage, rollbackReason); |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 125 | // Assume rollback executed successfully |
| 126 | return true; |
| 127 | } |
| 128 | |
| 129 | @Override |
| 130 | public String getName() { |
| 131 | return NAME; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 132 | } |
| 133 | |
| 134 | /** |
| 135 | * Start observing health of {@code packages} for {@code durationMs}. |
| 136 | * This may cause {@code packages} to be rolled back if they crash too freqeuntly. |
| 137 | */ |
| 138 | public void startObservingHealth(List<String> packages, long durationMs) { |
Zimuzo | caa435e | 2019-03-20 11:16:06 +0000 | [diff] [blame] | 139 | PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs); |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 140 | } |
| 141 | |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 142 | /** Verifies the rollback state after a reboot and schedules polling for sometime after reboot |
| 143 | * to check for native crashes and mitigate them if needed. |
| 144 | */ |
| 145 | public void onBootCompletedAsync() { |
| 146 | mHandler.post(()->onBootCompleted()); |
| 147 | } |
| 148 | |
| 149 | private void onBootCompleted() { |
| 150 | RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); |
| 151 | PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller(); |
| 152 | String moduleMetadataPackageName = getModuleMetadataPackageName(); |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 153 | |
Mohammad Samiul Islam | a3f650d | 2019-12-10 12:09:54 +0000 | [diff] [blame] | 154 | if (!rollbackManager.getAvailableRollbacks().isEmpty()) { |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 155 | scheduleCheckAndMitigateNativeCrashes(); |
| 156 | } |
| 157 | |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 158 | int rollbackId = popLastStagedRollbackId(); |
| 159 | if (rollbackId == INVALID_ROLLBACK_ID) { |
| 160 | // No staged rollback before reboot |
| 161 | return; |
| 162 | } |
| 163 | |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 164 | RollbackInfo rollback = null; |
| 165 | for (RollbackInfo info : rollbackManager.getRecentlyCommittedRollbacks()) { |
| 166 | if (rollbackId == info.getRollbackId()) { |
| 167 | rollback = info; |
| 168 | break; |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | if (rollback == null) { |
| 173 | Slog.e(TAG, "rollback info not found for last staged rollback: " + rollbackId); |
| 174 | return; |
| 175 | } |
| 176 | |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 177 | // Use the version of the metadata package that was installed before |
| 178 | // we rolled back for logging purposes. |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 179 | VersionedPackage oldModuleMetadataPackage = null; |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 180 | for (PackageRollbackInfo packageRollback : rollback.getPackages()) { |
Zimuzo | da97236 | 2019-04-27 19:57:58 +0100 | [diff] [blame] | 181 | if (packageRollback.getPackageName().equals(moduleMetadataPackageName)) { |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 182 | oldModuleMetadataPackage = packageRollback.getVersionRolledBackFrom(); |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 183 | break; |
| 184 | } |
| 185 | } |
| 186 | |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 187 | int sessionId = rollback.getCommittedSessionId(); |
| 188 | PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId); |
| 189 | if (sessionInfo == null) { |
| 190 | Slog.e(TAG, "On boot completed, could not load session id " + sessionId); |
| 191 | return; |
| 192 | } |
| 193 | if (sessionInfo.isStagedSessionApplied()) { |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 194 | logEvent(oldModuleMetadataPackage, |
Gavin Corkery | dd1daba | 2019-11-27 19:11:13 +0000 | [diff] [blame] | 195 | StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, |
| 196 | WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 197 | } else if (sessionInfo.isStagedSessionReady()) { |
| 198 | // TODO: What do for staged session ready but not applied |
| 199 | } else { |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 200 | logEvent(oldModuleMetadataPackage, |
Gavin Corkery | dd1daba | 2019-11-27 19:11:13 +0000 | [diff] [blame] | 201 | StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, |
| 202 | WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 203 | } |
| 204 | } |
| 205 | |
Mohammad Samiul Islam | 7bd8788 | 2019-11-21 17:16:41 +0000 | [diff] [blame] | 206 | private RollbackInfo getAvailableRollback(VersionedPackage failedPackage) { |
| 207 | RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); |
Zimuzo | c119780 | 2019-01-30 12:05:41 +0000 | [diff] [blame] | 208 | for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) { |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 209 | for (PackageRollbackInfo packageRollback : rollback.getPackages()) { |
Zimuzo | da97236 | 2019-04-27 19:57:58 +0100 | [diff] [blame] | 210 | boolean hasFailedPackage = packageRollback.getPackageName().equals( |
Zimuzo | 71d931e | 2019-02-01 13:08:16 +0000 | [diff] [blame] | 211 | failedPackage.getPackageName()) |
Zimuzo | 972e1cd | 2019-01-28 16:30:01 +0000 | [diff] [blame] | 212 | && packageRollback.getVersionRolledBackFrom().getVersionCode() |
Zimuzo | 71d931e | 2019-02-01 13:08:16 +0000 | [diff] [blame] | 213 | == failedPackage.getVersionCode(); |
Zimuzo | da97236 | 2019-04-27 19:57:58 +0100 | [diff] [blame] | 214 | if (hasFailedPackage) { |
| 215 | return rollback; |
| 216 | } |
Zimuzo | e5009cd | 2019-01-23 18:11:58 +0000 | [diff] [blame] | 217 | } |
| 218 | } |
| 219 | return null; |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 220 | } |
Zimuzo | 03eeb13 | 2019-01-30 15:13:56 +0000 | [diff] [blame] | 221 | |
Zimuzo | da97236 | 2019-04-27 19:57:58 +0100 | [diff] [blame] | 222 | @Nullable |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 223 | private String getModuleMetadataPackageName() { |
Zimuzo | 03eeb13 | 2019-01-30 15:13:56 +0000 | [diff] [blame] | 224 | String packageName = mContext.getResources().getString( |
| 225 | R.string.config_defaultModuleMetadataProvider); |
Zimuzo | 9e57ecb | 2019-02-04 15:34:08 +0000 | [diff] [blame] | 226 | if (TextUtils.isEmpty(packageName)) { |
Zimuzo | 03eeb13 | 2019-01-30 15:13:56 +0000 | [diff] [blame] | 227 | return null; |
| 228 | } |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 229 | return packageName; |
| 230 | } |
| 231 | |
Zimuzo | da97236 | 2019-04-27 19:57:58 +0100 | [diff] [blame] | 232 | @Nullable |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 233 | private VersionedPackage getModuleMetadataPackage() { |
| 234 | String packageName = getModuleMetadataPackageName(); |
| 235 | if (packageName == null) { |
| 236 | return null; |
| 237 | } |
Zimuzo | 03eeb13 | 2019-01-30 15:13:56 +0000 | [diff] [blame] | 238 | |
| 239 | try { |
| 240 | return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo( |
| 241 | packageName, 0 /* flags */).getLongVersionCode()); |
| 242 | } catch (PackageManager.NameNotFoundException e) { |
| 243 | Slog.w(TAG, "Module metadata provider not found"); |
| 244 | return null; |
| 245 | } |
| 246 | } |
Zimuzo | ff99176 | 2019-02-01 19:25:44 +0000 | [diff] [blame] | 247 | |
| 248 | private BroadcastReceiver listenForStagedSessionReady(RollbackManager rollbackManager, |
Mohammad Samiul Islam | 7bd8788 | 2019-11-21 17:16:41 +0000 | [diff] [blame] | 249 | int rollbackId, @Nullable VersionedPackage moduleMetadataPackage) { |
Zimuzo | ff99176 | 2019-02-01 19:25:44 +0000 | [diff] [blame] | 250 | BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() { |
| 251 | @Override |
| 252 | public void onReceive(Context context, Intent intent) { |
| 253 | handleStagedSessionChange(rollbackManager, |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 254 | rollbackId, this /* BroadcastReceiver */, moduleMetadataPackage); |
Zimuzo | ff99176 | 2019-02-01 19:25:44 +0000 | [diff] [blame] | 255 | } |
| 256 | }; |
| 257 | IntentFilter sessionUpdatedFilter = |
| 258 | new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED); |
| 259 | mContext.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter); |
| 260 | return sessionUpdatedReceiver; |
| 261 | } |
| 262 | |
| 263 | private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId, |
Mohammad Samiul Islam | 7bd8788 | 2019-11-21 17:16:41 +0000 | [diff] [blame] | 264 | BroadcastReceiver listener, @Nullable VersionedPackage moduleMetadataPackage) { |
Zimuzo | ff99176 | 2019-02-01 19:25:44 +0000 | [diff] [blame] | 265 | PackageInstaller packageInstaller = |
| 266 | mContext.getPackageManager().getPackageInstaller(); |
| 267 | List<RollbackInfo> recentRollbacks = |
| 268 | rollbackManager.getRecentlyCommittedRollbacks(); |
| 269 | for (int i = 0; i < recentRollbacks.size(); i++) { |
| 270 | RollbackInfo recentRollback = recentRollbacks.get(i); |
| 271 | int sessionId = recentRollback.getCommittedSessionId(); |
| 272 | if ((rollbackId == recentRollback.getRollbackId()) |
| 273 | && (sessionId != PackageInstaller.SessionInfo.INVALID_ID)) { |
| 274 | PackageInstaller.SessionInfo sessionInfo = |
| 275 | packageInstaller.getSessionInfo(sessionId); |
Zimuzo | 8d231b4 | 2019-05-16 18:09:23 +0100 | [diff] [blame] | 276 | if (sessionInfo.isStagedSessionReady() && markStagedSessionHandled(rollbackId)) { |
Zimuzo | ff99176 | 2019-02-01 19:25:44 +0000 | [diff] [blame] | 277 | mContext.unregisterReceiver(listener); |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 278 | saveLastStagedRollbackId(rollbackId); |
Zimuzo | da97236 | 2019-04-27 19:57:58 +0100 | [diff] [blame] | 279 | logEvent(moduleMetadataPackage, |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 280 | StatsLog |
Gavin Corkery | dd1daba | 2019-11-27 19:11:13 +0000 | [diff] [blame] | 281 | .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED, |
| 282 | WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, |
| 283 | ""); |
Zimuzo | ff99176 | 2019-02-01 19:25:44 +0000 | [diff] [blame] | 284 | mContext.getSystemService(PowerManager.class).reboot("Rollback staged install"); |
Zimuzo | 8d231b4 | 2019-05-16 18:09:23 +0100 | [diff] [blame] | 285 | } else if (sessionInfo.isStagedSessionFailed() |
| 286 | && markStagedSessionHandled(rollbackId)) { |
Zimuzo | da97236 | 2019-04-27 19:57:58 +0100 | [diff] [blame] | 287 | logEvent(moduleMetadataPackage, |
Gavin Corkery | dd1daba | 2019-11-27 19:11:13 +0000 | [diff] [blame] | 288 | StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, |
| 289 | WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, |
| 290 | ""); |
Zimuzo | ff99176 | 2019-02-01 19:25:44 +0000 | [diff] [blame] | 291 | mContext.unregisterReceiver(listener); |
| 292 | } |
| 293 | } |
| 294 | } |
| 295 | } |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 296 | |
Zimuzo | 8d231b4 | 2019-05-16 18:09:23 +0100 | [diff] [blame] | 297 | /** |
| 298 | * Returns {@code true} if staged session associated with {@code rollbackId} was marked |
| 299 | * as handled, {@code false} if already handled. |
| 300 | */ |
| 301 | private boolean markStagedSessionHandled(int rollbackId) { |
| 302 | synchronized (mPendingStagedRollbackIds) { |
| 303 | return mPendingStagedRollbackIds.remove(rollbackId); |
| 304 | } |
| 305 | } |
| 306 | |
Zimuzo | 841c494 | 2019-03-04 12:31:48 +0000 | [diff] [blame] | 307 | private void saveLastStagedRollbackId(int stagedRollbackId) { |
| 308 | try { |
| 309 | FileOutputStream fos = new FileOutputStream(mLastStagedRollbackIdFile); |
| 310 | PrintWriter pw = new PrintWriter(fos); |
| 311 | pw.println(stagedRollbackId); |
| 312 | pw.flush(); |
| 313 | FileUtils.sync(fos); |
| 314 | pw.close(); |
| 315 | } catch (IOException e) { |
| 316 | Slog.e(TAG, "Failed to save last staged rollback id", e); |
| 317 | mLastStagedRollbackIdFile.delete(); |
| 318 | } |
| 319 | } |
| 320 | |
| 321 | private int popLastStagedRollbackId() { |
| 322 | int rollbackId = INVALID_ROLLBACK_ID; |
| 323 | if (!mLastStagedRollbackIdFile.exists()) { |
| 324 | return rollbackId; |
| 325 | } |
| 326 | |
| 327 | try { |
| 328 | rollbackId = Integer.parseInt( |
| 329 | IoUtils.readFileAsString(mLastStagedRollbackIdFile.getAbsolutePath()).trim()); |
| 330 | } catch (IOException | NumberFormatException e) { |
| 331 | Slog.e(TAG, "Failed to retrieve last staged rollback id", e); |
| 332 | } |
| 333 | mLastStagedRollbackIdFile.delete(); |
| 334 | return rollbackId; |
| 335 | } |
Zimuzo | da97236 | 2019-04-27 19:57:58 +0100 | [diff] [blame] | 336 | |
Gavin Corkery | dd1daba | 2019-11-27 19:11:13 +0000 | [diff] [blame] | 337 | private static void logEvent(@Nullable VersionedPackage moduleMetadataPackage, int type, |
| 338 | int rollbackReason, @NonNull String failingPackageName) { |
Zimuzo | da97236 | 2019-04-27 19:57:58 +0100 | [diff] [blame] | 339 | Slog.i(TAG, "Watchdog event occurred of type: " + type); |
| 340 | if (moduleMetadataPackage != null) { |
Tej Singh | 73f8e9b | 2019-05-19 16:52:38 -0700 | [diff] [blame] | 341 | StatsLog.logWatchdogRollbackOccurred(type, moduleMetadataPackage.getPackageName(), |
Gavin Corkery | dd1daba | 2019-11-27 19:11:13 +0000 | [diff] [blame] | 342 | moduleMetadataPackage.getVersionCode(), rollbackReason, failingPackageName); |
Zimuzo | da97236 | 2019-04-27 19:57:58 +0100 | [diff] [blame] | 343 | } |
| 344 | } |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 345 | |
| 346 | /** |
| 347 | * This method should be only called on mHandler thread, since it modifies |
| 348 | * {@link #mNumberOfNativeCrashPollsRemaining} and we want to keep this class lock free. |
| 349 | */ |
| 350 | private void checkAndMitigateNativeCrashes() { |
| 351 | mNumberOfNativeCrashPollsRemaining--; |
| 352 | // Check if native watchdog reported a crash |
Nikita Ioffe | 76a0579 | 2019-10-04 16:51:06 +0100 | [diff] [blame] | 353 | if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) { |
Mohammad Samiul Islam | 7bd8788 | 2019-11-21 17:16:41 +0000 | [diff] [blame] | 354 | rollbackAll(); |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 355 | // we stop polling after an attempt to execute rollback, regardless of whether the |
| 356 | // attempt succeeds or not |
| 357 | } else { |
| 358 | if (mNumberOfNativeCrashPollsRemaining > 0) { |
| 359 | mHandler.postDelayed(() -> checkAndMitigateNativeCrashes(), |
| 360 | NATIVE_CRASH_POLLING_INTERVAL_MILLIS); |
| 361 | } |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | /** |
Mohammad Samiul Islam | 7bd8788 | 2019-11-21 17:16:41 +0000 | [diff] [blame] | 366 | * Returns true if the package name is the name of a module. |
| 367 | */ |
| 368 | private boolean isModule(String packageName) { |
| 369 | PackageManager pm = mContext.getPackageManager(); |
| 370 | try { |
| 371 | return pm.getModuleInfo(packageName, 0) != null; |
| 372 | } catch (PackageManager.NameNotFoundException ignore) { |
| 373 | return false; |
| 374 | } |
| 375 | } |
| 376 | |
| 377 | private VersionedPackage getVersionedPackage(String packageName) { |
| 378 | try { |
| 379 | return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo( |
| 380 | packageName, 0 /* flags */).getLongVersionCode()); |
| 381 | } catch (PackageManager.NameNotFoundException e) { |
| 382 | return null; |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | /** |
| 387 | * Rolls back the session that owns {@code failedPackage} |
| 388 | * |
| 389 | * @param rollback {@code rollbackInfo} of the {@code failedPackage} |
| 390 | * @param failedPackage the package that needs to be rolled back |
| 391 | */ |
| 392 | private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage, |
| 393 | @FailureReasons int rollbackReason) { |
| 394 | final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); |
| 395 | int reasonToLog = mapFailureReasonToMetric(rollbackReason); |
Gavin Corkery | cf6a399 | 2019-12-10 19:48:50 +0000 | [diff] [blame] | 396 | final String failedPackageToLog; |
| 397 | if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { |
| 398 | failedPackageToLog = SystemProperties.get( |
| 399 | "sys.init.updatable_crashing_process_name", ""); |
| 400 | } else { |
| 401 | failedPackageToLog = failedPackage.getPackageName(); |
| 402 | } |
Mohammad Samiul Islam | 7bd8788 | 2019-11-21 17:16:41 +0000 | [diff] [blame] | 403 | final VersionedPackage logPackage = isModule(failedPackage.getPackageName()) |
| 404 | ? getModuleMetadataPackage() |
| 405 | : null; |
| 406 | |
| 407 | logEvent(logPackage, |
| 408 | StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE, |
Gavin Corkery | cf6a399 | 2019-12-10 19:48:50 +0000 | [diff] [blame] | 409 | reasonToLog, failedPackageToLog); |
Mohammad Samiul Islam | 7bd8788 | 2019-11-21 17:16:41 +0000 | [diff] [blame] | 410 | final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> { |
| 411 | int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, |
| 412 | RollbackManager.STATUS_FAILURE); |
| 413 | if (status == RollbackManager.STATUS_SUCCESS) { |
| 414 | if (rollback.isStaged()) { |
| 415 | int rollbackId = rollback.getRollbackId(); |
| 416 | synchronized (mPendingStagedRollbackIds) { |
| 417 | mPendingStagedRollbackIds.add(rollbackId); |
| 418 | } |
| 419 | BroadcastReceiver listener = |
| 420 | listenForStagedSessionReady(rollbackManager, rollbackId, |
| 421 | logPackage); |
| 422 | handleStagedSessionChange(rollbackManager, rollbackId, listener, |
| 423 | logPackage); |
| 424 | } else { |
| 425 | logEvent(logPackage, |
| 426 | StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, |
Gavin Corkery | cf6a399 | 2019-12-10 19:48:50 +0000 | [diff] [blame] | 427 | reasonToLog, failedPackageToLog); |
Mohammad Samiul Islam | 7bd8788 | 2019-11-21 17:16:41 +0000 | [diff] [blame] | 428 | } |
| 429 | } else { |
| 430 | logEvent(logPackage, |
| 431 | StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, |
Gavin Corkery | cf6a399 | 2019-12-10 19:48:50 +0000 | [diff] [blame] | 432 | reasonToLog, failedPackageToLog); |
Mohammad Samiul Islam | 7bd8788 | 2019-11-21 17:16:41 +0000 | [diff] [blame] | 433 | } |
| 434 | }); |
| 435 | |
| 436 | mHandler.post(() -> |
| 437 | rollbackManager.commitRollback(rollback.getRollbackId(), |
| 438 | Collections.singletonList(failedPackage), |
| 439 | rollbackReceiver.getIntentSender())); |
| 440 | } |
| 441 | |
| 442 | private void rollbackAll() { |
| 443 | Slog.i(TAG, "Rolling back all available rollbacks"); |
| 444 | RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); |
| 445 | List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks(); |
| 446 | |
| 447 | for (RollbackInfo rollback : rollbacks) { |
| 448 | String samplePackageName = rollback.getPackages().get(0).getPackageName(); |
| 449 | VersionedPackage sampleVersionedPackage = getVersionedPackage(samplePackageName); |
| 450 | if (sampleVersionedPackage == null) { |
| 451 | Slog.e(TAG, "Failed to rollback " + samplePackageName); |
| 452 | continue; |
| 453 | } |
| 454 | rollbackPackage(rollback, sampleVersionedPackage, |
| 455 | PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); |
| 456 | } |
| 457 | } |
| 458 | |
| 459 | /** |
shafik | c5805fb9 | 2019-04-29 20:08:07 +0100 | [diff] [blame] | 460 | * Since this method can eventually trigger a RollbackManager rollback, it should be called |
| 461 | * only once boot has completed {@code onBootCompleted} and not earlier, because the install |
| 462 | * session must be entirely completed before we try to rollback. |
| 463 | */ |
| 464 | private void scheduleCheckAndMitigateNativeCrashes() { |
| 465 | Slog.i(TAG, "Scheduling " + mNumberOfNativeCrashPollsRemaining + " polls to check " |
| 466 | + "and mitigate native crashes"); |
| 467 | mHandler.post(()->checkAndMitigateNativeCrashes()); |
| 468 | } |
Gavin Corkery | dd1daba | 2019-11-27 19:11:13 +0000 | [diff] [blame] | 469 | |
| 470 | private int mapFailureReasonToMetric(@FailureReasons int failureReason) { |
| 471 | switch (failureReason) { |
| 472 | case PackageWatchdog.FAILURE_REASON_NATIVE_CRASH: |
| 473 | return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH; |
| 474 | case PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK: |
| 475 | return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK; |
| 476 | case PackageWatchdog.FAILURE_REASON_APP_CRASH: |
| 477 | return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; |
| 478 | case PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING: |
| 479 | return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; |
| 480 | default: |
| 481 | return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN; |
| 482 | } |
| 483 | } |
| 484 | |
Zimuzo | c4073cc | 2019-01-18 18:39:18 +0000 | [diff] [blame] | 485 | } |