blob: 9348806c0e596be0866691e96ab4fbaf79057e90 [file] [log] [blame]
Zimuzoc4073cc2019-01-18 18:39:18 +00001/*
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
17package com.android.server.rollback;
18
Zimuzoff991762019-02-01 19:25:44 +000019import android.content.BroadcastReceiver;
Zimuzoc4073cc2019-01-18 18:39:18 +000020import android.content.Context;
21import android.content.Intent;
Zimuzoff991762019-02-01 19:25:44 +000022import android.content.IntentFilter;
23import android.content.pm.PackageInstaller;
Zimuzo03eeb132019-01-30 15:13:56 +000024import android.content.pm.PackageManager;
Zimuzoc1197802019-01-30 12:05:41 +000025import android.content.pm.VersionedPackage;
Richard Uhler0e961922019-01-25 13:07:08 +000026import android.content.rollback.PackageRollbackInfo;
Zimuzoc4073cc2019-01-18 18:39:18 +000027import android.content.rollback.RollbackInfo;
28import android.content.rollback.RollbackManager;
Zimuzo841c4942019-03-04 12:31:48 +000029import android.os.Environment;
30import android.os.FileUtils;
Zimuzoc4073cc2019-01-18 18:39:18 +000031import android.os.Handler;
32import android.os.HandlerThread;
Zimuzoff991762019-02-01 19:25:44 +000033import android.os.PowerManager;
Zimuzo03eeb132019-01-30 15:13:56 +000034import android.text.TextUtils;
Zimuzo9e57ecb2019-02-04 15:34:08 +000035import android.util.Pair;
Zimuzo03eeb132019-01-30 15:13:56 +000036import android.util.Slog;
37import android.util.StatsLog;
Zimuzoc4073cc2019-01-18 18:39:18 +000038
Zimuzo03eeb132019-01-30 15:13:56 +000039import com.android.internal.R;
Zimuzoc4073cc2019-01-18 18:39:18 +000040import com.android.server.PackageWatchdog;
41import com.android.server.PackageWatchdog.PackageHealthObserver;
Zimuzoe5009cd2019-01-23 18:11:58 +000042import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
Zimuzoc4073cc2019-01-18 18:39:18 +000043
Zimuzo841c4942019-03-04 12:31:48 +000044import libcore.io.IoUtils;
45
46import java.io.File;
47import java.io.FileOutputStream;
48import java.io.IOException;
49import java.io.PrintWriter;
Richard Uhlerbf5b5c42019-01-28 15:26:37 +000050import java.util.Collections;
Zimuzoc4073cc2019-01-18 18:39:18 +000051import java.util.List;
52
53/**
54 * {@code PackageHealthObserver} for {@code RollbackManagerService}.
55 *
56 * @hide
57 */
58public final class RollbackPackageHealthObserver implements PackageHealthObserver {
59 private static final String TAG = "RollbackPackageHealthObserver";
60 private static final String NAME = "rollback-observer";
Zimuzo841c4942019-03-04 12:31:48 +000061 private static final int INVALID_ROLLBACK_ID = -1;
62 private final Context mContext;
63 private final Handler mHandler;
64 private final File mLastStagedRollbackIdFile;
Zimuzoc4073cc2019-01-18 18:39:18 +000065
66 RollbackPackageHealthObserver(Context context) {
67 mContext = context;
68 HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver");
69 handlerThread.start();
70 mHandler = handlerThread.getThreadHandler();
Zimuzo841c4942019-03-04 12:31:48 +000071 File dataDir = new File(Environment.getDataDirectory(), "rollback-observer");
72 dataDir.mkdirs();
73 mLastStagedRollbackIdFile = new File(dataDir, "last-staged-rollback-id");
Zimuzoc4073cc2019-01-18 18:39:18 +000074 PackageWatchdog.getInstance(mContext).registerHealthObserver(this);
75 }
76
77 @Override
Zimuzo71d931e2019-02-01 13:08:16 +000078 public int onHealthCheckFailed(VersionedPackage failedPackage) {
Zimuzo03eeb132019-01-30 15:13:56 +000079 VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
80 if (moduleMetadataPackage == null) {
81 // Ignore failure, no mainline update available
82 return PackageHealthObserverImpact.USER_IMPACT_NONE;
83 }
84
Zimuzo9e57ecb2019-02-04 15:34:08 +000085 if (getAvailableRollback(mContext.getSystemService(RollbackManager.class),
86 failedPackage, moduleMetadataPackage) == null) {
Zimuzoe5009cd2019-01-23 18:11:58 +000087 // Don't handle the notification, no rollbacks available for the package
88 return PackageHealthObserverImpact.USER_IMPACT_NONE;
Zimuzoc4073cc2019-01-18 18:39:18 +000089 }
Zimuzoe5009cd2019-01-23 18:11:58 +000090 // Rollback is available, we may get a callback into #execute
91 return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
92 }
93
94 @Override
Zimuzo71d931e2019-02-01 13:08:16 +000095 public boolean execute(VersionedPackage failedPackage) {
Zimuzo03eeb132019-01-30 15:13:56 +000096 VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
97 if (moduleMetadataPackage == null) {
98 // Ignore failure, no mainline update available
Zimuzoe5009cd2019-01-23 18:11:58 +000099 return false;
100 }
101
Zimuzo03eeb132019-01-30 15:13:56 +0000102 RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
Zimuzo9e57ecb2019-02-04 15:34:08 +0000103 Pair<RollbackInfo, Boolean> rollbackPair = getAvailableRollback(rollbackManager,
Zimuzo71d931e2019-02-01 13:08:16 +0000104 failedPackage, moduleMetadataPackage);
Zimuzo9e57ecb2019-02-04 15:34:08 +0000105 if (rollbackPair == null) {
106 Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ "
Zimuzo71d931e2019-02-01 13:08:16 +0000107 + failedPackage.getPackageName() + "] with versionCode: ["
108 + failedPackage.getVersionCode() + "]");
Zimuzo03eeb132019-01-30 15:13:56 +0000109 return false;
110 }
Zimuzoff991762019-02-01 19:25:44 +0000111
Zimuzo9e57ecb2019-02-04 15:34:08 +0000112 RollbackInfo rollback = rollbackPair.first;
113 // We only log mainline package rollbacks, so check if rollback contains the
114 // module metadata provider, if it does, the rollback is a mainline rollback
115 boolean hasModuleMetadataPackage = rollbackPair.second;
Zimuzo03eeb132019-01-30 15:13:56 +0000116
Zimuzo9e57ecb2019-02-04 15:34:08 +0000117 if (hasModuleMetadataPackage) {
118 StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
119 StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
120 moduleMetadataPackage.getPackageName(),
121 moduleMetadataPackage.getVersionCode());
122 }
Zimuzoe5009cd2019-01-23 18:11:58 +0000123 LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
Zimuzo9e57ecb2019-02-04 15:34:08 +0000124 if (hasModuleMetadataPackage) {
125 int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
126 RollbackManager.STATUS_FAILURE);
127 if (status == RollbackManager.STATUS_SUCCESS) {
Zimuzoff991762019-02-01 19:25:44 +0000128 if (rollback.isStaged()) {
129 int rollbackId = rollback.getRollbackId();
130 BroadcastReceiver listener =
Zimuzo841c4942019-03-04 12:31:48 +0000131 listenForStagedSessionReady(rollbackManager, rollbackId,
132 moduleMetadataPackage);
133 handleStagedSessionChange(rollbackManager, rollbackId, listener,
134 moduleMetadataPackage);
135 } else {
136 StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
137 StatsLog
138 .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
139 moduleMetadataPackage.getPackageName(),
140 moduleMetadataPackage.getVersionCode());
Zimuzoff991762019-02-01 19:25:44 +0000141 }
Zimuzo9e57ecb2019-02-04 15:34:08 +0000142 } else {
143 StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
144 StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
145 moduleMetadataPackage.getPackageName(),
146 moduleMetadataPackage.getVersionCode());
147 }
Zimuzoe5009cd2019-01-23 18:11:58 +0000148 }
149 });
150
Zimuzoe5009cd2019-01-23 18:11:58 +0000151 mHandler.post(() ->
Zimuzoc1197802019-01-30 12:05:41 +0000152 rollbackManager.commitRollback(rollback.getRollbackId(),
Zimuzo9e57ecb2019-02-04 15:34:08 +0000153 Collections.singletonList(failedPackage),
Richard Uhlere87368e2019-01-24 16:34:14 +0000154 rollbackReceiver.getIntentSender()));
Zimuzoe5009cd2019-01-23 18:11:58 +0000155 // Assume rollback executed successfully
156 return true;
157 }
158
159 @Override
160 public String getName() {
161 return NAME;
Zimuzoc4073cc2019-01-18 18:39:18 +0000162 }
163
164 /**
165 * Start observing health of {@code packages} for {@code durationMs}.
166 * This may cause {@code packages} to be rolled back if they crash too freqeuntly.
167 */
168 public void startObservingHealth(List<String> packages, long durationMs) {
Zimuzo9284e742019-02-22 12:09:28 +0000169 PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs,
170 false /* withExplicitHealthCheck */);
Zimuzoc4073cc2019-01-18 18:39:18 +0000171 }
172
Zimuzo841c4942019-03-04 12:31:48 +0000173 /** Verifies the rollback state after a reboot. */
174 public void onBootCompleted() {
175 int rollbackId = popLastStagedRollbackId();
176 if (rollbackId == INVALID_ROLLBACK_ID) {
177 // No staged rollback before reboot
178 return;
179 }
180
181 RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
182 PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
183 RollbackInfo rollback = null;
184 for (RollbackInfo info : rollbackManager.getRecentlyCommittedRollbacks()) {
185 if (rollbackId == info.getRollbackId()) {
186 rollback = info;
187 break;
188 }
189 }
190
191 if (rollback == null) {
192 Slog.e(TAG, "rollback info not found for last staged rollback: " + rollbackId);
193 return;
194 }
195
196 String moduleMetadataPackageName = getModuleMetadataPackageName();
197 if (moduleMetadataPackageName == null) {
198 // Only log mainline staged rollbacks
199 return;
200 }
201
202 // Use the version of the metadata package that was installed before
203 // we rolled back for logging purposes.
204 VersionedPackage moduleMetadataPackage = null;
205 for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
206 if (moduleMetadataPackageName.equals(packageRollback.getPackageName())) {
207 moduleMetadataPackage = packageRollback.getVersionRolledBackFrom();
208 break;
209 }
210 }
211
212 if (moduleMetadataPackage == null) {
213 // Only log mainline staged rollbacks
214 return;
215 }
216
217 int sessionId = rollback.getCommittedSessionId();
218 PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);
219 if (sessionInfo == null) {
220 Slog.e(TAG, "On boot completed, could not load session id " + sessionId);
221 return;
222 }
223 if (sessionInfo.isStagedSessionApplied()) {
224 StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
225 StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
226 moduleMetadataPackage.getPackageName(),
227 moduleMetadataPackage.getVersionCode());
228 } else if (sessionInfo.isStagedSessionReady()) {
229 // TODO: What do for staged session ready but not applied
230 } else {
231 StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
232 StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
233 moduleMetadataPackage.getPackageName(),
234 moduleMetadataPackage.getVersionCode());
235 }
236 }
237
Zimuzo9e57ecb2019-02-04 15:34:08 +0000238 private Pair<RollbackInfo, Boolean> getAvailableRollback(RollbackManager rollbackManager,
Zimuzo71d931e2019-02-01 13:08:16 +0000239 VersionedPackage failedPackage, VersionedPackage moduleMetadataPackage) {
Zimuzoc1197802019-01-30 12:05:41 +0000240 for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
Zimuzo03eeb132019-01-30 15:13:56 +0000241 // We only rollback mainline packages, so check if rollback contains the
242 // module metadata provider, if it does, the rollback is a mainline rollback
243 boolean hasModuleMetadataPackage = false;
244 boolean hasFailedPackage = false;
Zimuzoe5009cd2019-01-23 18:11:58 +0000245 for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
Zimuzo03eeb132019-01-30 15:13:56 +0000246 hasModuleMetadataPackage |= packageRollback.getPackageName().equals(
Zimuzo71d931e2019-02-01 13:08:16 +0000247 moduleMetadataPackage.getPackageName());
248 hasFailedPackage |= packageRollback.getPackageName().equals(
249 failedPackage.getPackageName())
Zimuzo972e1cd2019-01-28 16:30:01 +0000250 && packageRollback.getVersionRolledBackFrom().getVersionCode()
Zimuzo71d931e2019-02-01 13:08:16 +0000251 == failedPackage.getVersionCode();
Zimuzo03eeb132019-01-30 15:13:56 +0000252 }
Zimuzo9e57ecb2019-02-04 15:34:08 +0000253 if (hasFailedPackage) {
254 return new Pair<RollbackInfo, Boolean>(rollback, hasModuleMetadataPackage);
Zimuzoe5009cd2019-01-23 18:11:58 +0000255 }
256 }
257 return null;
Zimuzoc4073cc2019-01-18 18:39:18 +0000258 }
Zimuzo03eeb132019-01-30 15:13:56 +0000259
Zimuzo841c4942019-03-04 12:31:48 +0000260 private String getModuleMetadataPackageName() {
Zimuzo03eeb132019-01-30 15:13:56 +0000261 String packageName = mContext.getResources().getString(
262 R.string.config_defaultModuleMetadataProvider);
Zimuzo9e57ecb2019-02-04 15:34:08 +0000263 if (TextUtils.isEmpty(packageName)) {
Zimuzo03eeb132019-01-30 15:13:56 +0000264 return null;
265 }
Zimuzo841c4942019-03-04 12:31:48 +0000266 return packageName;
267 }
268
269 private VersionedPackage getModuleMetadataPackage() {
270 String packageName = getModuleMetadataPackageName();
271 if (packageName == null) {
272 return null;
273 }
Zimuzo03eeb132019-01-30 15:13:56 +0000274
275 try {
276 return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo(
277 packageName, 0 /* flags */).getLongVersionCode());
278 } catch (PackageManager.NameNotFoundException e) {
279 Slog.w(TAG, "Module metadata provider not found");
280 return null;
281 }
282 }
Zimuzoff991762019-02-01 19:25:44 +0000283
284 private BroadcastReceiver listenForStagedSessionReady(RollbackManager rollbackManager,
Zimuzo841c4942019-03-04 12:31:48 +0000285 int rollbackId, VersionedPackage moduleMetadataPackage) {
Zimuzoff991762019-02-01 19:25:44 +0000286 BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
287 @Override
288 public void onReceive(Context context, Intent intent) {
289 handleStagedSessionChange(rollbackManager,
Zimuzo841c4942019-03-04 12:31:48 +0000290 rollbackId, this /* BroadcastReceiver */, moduleMetadataPackage);
Zimuzoff991762019-02-01 19:25:44 +0000291 }
292 };
293 IntentFilter sessionUpdatedFilter =
294 new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED);
295 mContext.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter);
296 return sessionUpdatedReceiver;
297 }
298
299 private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId,
Zimuzo841c4942019-03-04 12:31:48 +0000300 BroadcastReceiver listener, VersionedPackage moduleMetadataPackage) {
Zimuzoff991762019-02-01 19:25:44 +0000301 PackageInstaller packageInstaller =
302 mContext.getPackageManager().getPackageInstaller();
303 List<RollbackInfo> recentRollbacks =
304 rollbackManager.getRecentlyCommittedRollbacks();
305 for (int i = 0; i < recentRollbacks.size(); i++) {
306 RollbackInfo recentRollback = recentRollbacks.get(i);
307 int sessionId = recentRollback.getCommittedSessionId();
308 if ((rollbackId == recentRollback.getRollbackId())
309 && (sessionId != PackageInstaller.SessionInfo.INVALID_ID)) {
310 PackageInstaller.SessionInfo sessionInfo =
311 packageInstaller.getSessionInfo(sessionId);
Dario Freni60a96c12019-02-24 21:01:29 +0000312 if (sessionInfo.isStagedSessionReady()) {
Zimuzoff991762019-02-01 19:25:44 +0000313 mContext.unregisterReceiver(listener);
Zimuzo841c4942019-03-04 12:31:48 +0000314 saveLastStagedRollbackId(rollbackId);
315 StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
316 StatsLog
317 .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
318 moduleMetadataPackage.getPackageName(),
319 moduleMetadataPackage.getVersionCode());
Zimuzoff991762019-02-01 19:25:44 +0000320 mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
Dario Freni60a96c12019-02-24 21:01:29 +0000321 } else if (sessionInfo.isStagedSessionFailed()) {
Zimuzo841c4942019-03-04 12:31:48 +0000322 StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
323 StatsLog
324 .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
325 moduleMetadataPackage.getPackageName(),
326 moduleMetadataPackage.getVersionCode());
Zimuzoff991762019-02-01 19:25:44 +0000327 mContext.unregisterReceiver(listener);
328 }
329 }
330 }
331 }
Zimuzo841c4942019-03-04 12:31:48 +0000332
333 private void saveLastStagedRollbackId(int stagedRollbackId) {
334 try {
335 FileOutputStream fos = new FileOutputStream(mLastStagedRollbackIdFile);
336 PrintWriter pw = new PrintWriter(fos);
337 pw.println(stagedRollbackId);
338 pw.flush();
339 FileUtils.sync(fos);
340 pw.close();
341 } catch (IOException e) {
342 Slog.e(TAG, "Failed to save last staged rollback id", e);
343 mLastStagedRollbackIdFile.delete();
344 }
345 }
346
347 private int popLastStagedRollbackId() {
348 int rollbackId = INVALID_ROLLBACK_ID;
349 if (!mLastStagedRollbackIdFile.exists()) {
350 return rollbackId;
351 }
352
353 try {
354 rollbackId = Integer.parseInt(
355 IoUtils.readFileAsString(mLastStagedRollbackIdFile.getAbsolutePath()).trim());
356 } catch (IOException | NumberFormatException e) {
357 Slog.e(TAG, "Failed to retrieve last staged rollback id", e);
358 }
359 mLastStagedRollbackIdFile.delete();
360 return rollbackId;
361 }
Zimuzoc4073cc2019-01-18 18:39:18 +0000362}