Android RollBack機制實現原理剖析
轉載 https://blog.csdn.net/ChaoY1116/article/details/109143954
功能介紹:
在Android 10.0中,Google新增加了個功能。
如果使用者對新升級的APP不滿意,可以通過“回到過去”,回滾到舊版。
當然,如果新安裝的apk出現了各種問題無法使用,也可以進行回滾的操作。
這樣的確可以極大的提升使用者的體驗,但是因為這塊的邏輯較複雜,我們以module_crash_rollback_test為例,來看下具體的實現邏輯。
程式碼路徑如下:
./base/services/core/java/com/android/server/rollback
./base/core/java/android/content/rollback
工作原理:
如何驗證這個功能是否邏輯生效,我們可以使用這個方法:
1. adb install -r -d --enable-rollback --staged ***.apk
2. adb reboot
3. adb shell dumpsys rollback
4. adb root
5. adb shell am crash *** (10 times)
6. adb reboot
7. adb wait-for-devices 1 mins
8. adb shell dumpsys rollback
我們即可從RollBack的狀態,檢查rollback機制是否被啟用以及使用。
dumpsys的code在程式碼中對應如下:
程式碼路徑為:frameworks/base/services/core/java/com/android/server/rollback/Rollback.java
void dump(IndentingPrintWriter ipw) {
synchronized (mLock) {
ipw.println(info.getRollbackId() + ":");
ipw.increaseIndent();
ipw.println("-state: " + getStateAsString());
ipw.println("-timestamp: " + getTimestamp());
if (getStagedSessionId() != -1) {
ipw.println("-stagedSessionId: " + getStagedSessionId());
}
ipw.println("-packages:");
ipw.increaseIndent();
for (PackageRollbackInfo pkg : info.getPackages()) {
ipw.println(pkg.getPackageName()
+ " " + pkg.getVersionRolledBackFrom().getLongVersionCode()
+ " -> " + pkg.getVersionRolledBackTo().getLongVersionCode());
}
ipw.decreaseIndent();
if (isCommitted()) {
ipw.println("-causePackages:");
ipw.increaseIndent();
for (VersionedPackage cPkg : info.getCausePackages()) {
ipw.println(cPkg.getPackageName() + " " + cPkg.getLongVersionCode());
}
ipw.decreaseIndent();
ipw.println("-committedSessionId: " + info.getCommittedSessionId());
}
if (mExtensionVersions.size() > 0) {
ipw.println("-extensionVersions:");
ipw.increaseIndent();
ipw.println(mExtensionVersions.toString());
ipw.decreaseIndent();
}
ipw.decreaseIndent();
}
}
從dumpsys中,我們就可以看到rollback的當前執行狀態。
String getStateAsString() {
synchronized (mLock) {
return rollbackStateToString(mState);
}
}
邏輯很簡單,即為將RollBack中的mState變數值置為String並且打出。
變數定義如下:
/**
* The current state of the rollback.
* ENABLING, AVAILABLE, or COMMITTED.
*/
@GuardedBy("mLock")
private @RollbackState int mState;
會有四個狀態值,來對應當前的mState.
@IntDef(prefix = { "ROLLBACK_STATE_" }, value = {
ROLLBACK_STATE_ENABLING,
ROLLBACK_STATE_AVAILABLE,
ROLLBACK_STATE_COMMITTED,
ROLLBACK_STATE_DELETED
})
那麼在執行module_crash_rollback_test的時候,我們的邏輯是怎麼生效的呢?
首先是在rollbackmanagerservice中:
/**
* Service that manages APK level rollbacks. Publishes
* Context.ROLLBACK_SERVICE.
*
* @hide
*/
public final class RollbackManagerService extends SystemService {
private RollbackManagerServiceImpl mService;
public RollbackManagerService(Context context) {
super(context);
}
@Override
public void onStart() {
mService = new RollbackManagerServiceImpl(getContext());
publishBinderService(Context.ROLLBACK_SERVICE, mService);
}
@Override
public void onUnlockUser(int user) {
mService.onUnlockUser(user);
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_BOOT_COMPLETED) {
mService.onBootCompleted();
}
}
可以看到的是,在PHASE_BOOT_COMPLETED時,將會呼叫onBootCompleted的函式。
如果看過之前的文章的同學,可能也明白了這個函式是在系統啟動完成後,針對全域性發出的通知。
@AnyThread
void onBootCompleted() {
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
mExecutor, properties -> updateRollbackLifetimeDurationInMillis());
getHandler().post(() -> {
updateRollbackLifetimeDurationInMillis();
runExpiration();
// Check to see if any rollback-enabled staged sessions or staged
// rollback sessions been applied.
List<Rollback> enabling = new ArrayList<>();
List<Rollback> restoreInProgress = new ArrayList<>();
Set<String> apexPackageNames = new HashSet<>();
synchronized (mLock) {
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
if (!rollback.isStaged()) {
// We only care about staged rollbacks here
continue;
}
PackageInstaller.SessionInfo session = mContext.getPackageManager()
.getPackageInstaller().getSessionInfo(rollback.getStagedSessionId());
if (session == null || session.isStagedSessionFailed()) {
iter.remove();
rollback.delete(mAppDataRollbackHelper);
continue;
}
if (session.isStagedSessionApplied()) {
if (rollback.isEnabling()) {
enabling.add(rollback);
} else if (rollback.isRestoreUserDataInProgress()) {
restoreInProgress.add(rollback);
}
}
apexPackageNames.addAll(rollback.getApexPackageNames());
}
}
for (Rollback rollback : enabling) {
makeRollbackAvailable(rollback);
}
for (Rollback rollback : restoreInProgress) {
rollback.setRestoreUserDataInProgress(false);
}
for (String apexPackageName : apexPackageNames) {
// We will not recieve notifications when an apex is updated,
// so check now in case any rollbacks ought to be expired. The
// onPackagedReplace function is safe to call if the package
// hasn't actually been updated.
onPackageReplaced(apexPackageName);
}
synchronized (mLock) {
mOrphanedApkSessionIds.clear();
}
mPackageHealthObserver.onBootCompletedAsync();
});
}
這段主要說的是在系統啟動過程中,我們將會對rollback的功能開啟,各個session的狀態,以及實際的packageName進行replaced,restore userdata的操作。這邊分析一下onPackageReplaced函式:
/**
* Called when a package has been replaced with a different version.
* Removes all backups for the package not matching the currently
* installed package version.
*/
@WorkerThread
private void onPackageReplaced(String packageName) {
// TODO: Could this end up incorrectly deleting a rollback for a
// package that is about to be installed?
long installedVersion = getInstalledPackageVersion(packageName);
synchronized (mLock) {
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
// TODO: Should we remove rollbacks in the ENABLING state here?
if ((rollback.isEnabling() || rollback.isAvailable())
&& rollback.includesPackageWithDifferentVersion(packageName,
installedVersion)) {
iter.remove();
rollback.delete(mAppDataRollbackHelper);
}
}
}
}
當包被其他版本替換時呼叫時,我們會通過installedVersion來儲存APK的版本號,
並且在下面將會刪除與當前安裝的包版本不匹配的包的所有備份。
在操作完,將會執行onBootCompletedAsync函式,而這邊是進行的通知。
/** Verifies the rollback state after a reboot and schedules polling for sometime after reboot
* to check for native crashes and mitigate them if needed.
*/
public void onBootCompletedAsync() {
mHandler.post(()->onBootCompleted());
}
那麼這個onBootCompleted在做什麼工作呢?
private void onBootCompleted() {
RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
if (!rollbackManager.getAvailableRollbacks().isEmpty()) {
// TODO(gavincorkery): Call into Package Watchdog from outside the observer
PackageWatchdog.getInstance(mContext).scheduleCheckAndMitigateNativeCrashes();
}
SparseArray<String> rollbackIds = popLastStagedRollbackIds();
for (int i = 0; i < rollbackIds.size(); i++) {
WatchdogRollbackLogger.logRollbackStatusOnBoot(mContext,
rollbackIds.keyAt(i), rollbackIds.valueAt(i),
rollbackManager.getRecentlyCommittedRollbacks());
}
}
這邊的重頭戲來了,scheduleCheckAndMitigateNativeCrashes看上去和我們要驗證的module_crash_rollback_test非常的相似。
/**
* Since this method can eventually trigger a rollback, it should be called
* only once boot has completed {@code onBootCompleted} and not earlier, because the install
* session must be entirely completed before we try to rollback.
*/
public void scheduleCheckAndMitigateNativeCrashes() {
Slog.i(TAG, "Scheduling " + mNumberOfNativeCrashPollsRemaining + " polls to check "
+ "and mitigate native crashes");
mShortTaskHandler.post(()->checkAndMitigateNativeCrashes());
}
只是列印了log,就來執行check的操作。
/**
* This method should be only called on mShortTaskHandler, since it modifies
* {@link #mNumberOfNativeCrashPollsRemaining}.
*/
private void checkAndMitigateNativeCrashes() {
mNumberOfNativeCrashPollsRemaining--;
// Check if native watchdog reported a crash
if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) {
// We rollback everything available when crash is unattributable
onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH);
// we stop polling after an attempt to execute rollback, regardless of whether the
// attempt succeeds or not
} else {
if (mNumberOfNativeCrashPollsRemaining > 0) {
mShortTaskHandler.postDelayed(() -> checkAndMitigateNativeCrashes(),
NATIVE_CRASH_POLLING_INTERVAL_MILLIS);
}
}
}
這邊就非常奇怪了,為什麼會有sys.init.updatable_crashing這個systemproperties呢?
這個properties是定義在什麼地方?
程式碼路徑: system/core/init/service.cpp
// If we crash > 4 times in 4 minutes or before boot_completed,
// reboot into bootloader or set crashing property
boot_clock::time_point now = boot_clock::now();
if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART)) {
bool boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
if (now < time_crashed_ + 4min || !boot_completed) {
if (++crash_count_ > 4) {
if (flags_ & SVC_CRITICAL) {
// Aborts into bootloader
LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
<< (boot_completed ? "in 4 minutes" : "before boot completed");
} else {
LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
<< (boot_completed ? "in 4 minutes" : "before boot completed");
// Notifies update_verifier and apexd
SetProperty("sys.init.updatable_crashing_process_name", name_);
SetProperty("sys.init.updatable_crashing", "1");
}
}
} else {
time_crashed_ = now;
crash_count_ = 1;
}
}
這裡其實是對Crash的一個檢查,如果在開機以後,規定時間內有四次以上的crash,然後就會觸發這個properties的定義。
同時會記錄當前程式的名字:sys.init.updatable_crashing_process_name。
但是在正常的過程中,這個應該不會出現。
但是在我們之前測試步驟中,當我們連續crash apk多次,那麼重啟後是否就會啟用rollback呢?
應該是的,我們繼續看看狀態的改變過程。
/**
* Called when a process fails due to a crash, ANR or explicit health check.
*
* <p>For each package contained in the process, one registered observer with the least user
* impact will be notified for mitigation.
*
* <p>This method could be called frequently if there is a severe problem on the device.
*/
public void onPackageFailure(List<VersionedPackage> packages,
@FailureReasons int failureReason) {
if (packages == null) {
Slog.w(TAG, "Could not resolve a list of failing packages");
return;
}
mLongTaskHandler.post(() -> {
synchronized (mLock) {
if (mAllObservers.isEmpty()) {
return;
}
boolean requiresImmediateAction = (failureReason == FAILURE_REASON_NATIVE_CRASH
|| failureReason == FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
if (requiresImmediateAction) {
handleFailureImmediately(packages, failureReason);
} else {
for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
VersionedPackage versionedPackage = packages.get(pIndex);
// Observer that will receive failure for versionedPackage
PackageHealthObserver currentObserverToNotify = null;
int currentObserverImpact = Integer.MAX_VALUE;
// Find observer with least user impact
for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
ObserverInternal observer = mAllObservers.valueAt(oIndex);
PackageHealthObserver registeredObserver = observer.registeredObserver;
if (registeredObserver != null
&& observer.onPackageFailureLocked(
versionedPackage.getPackageName())) {
int impact = registeredObserver.onHealthCheckFailed(
versionedPackage, failureReason);
if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
&& impact < currentObserverImpact) {
currentObserverToNotify = registeredObserver;
currentObserverImpact = impact;
}
}
}
// Execute action with least user impact
if (currentObserverToNotify != null) {
currentObserverToNotify.execute(versionedPackage, failureReason);
}
}
}
}
});
}
當package failureReason 的原因為Native_Crash和FAILURE_REASON_EXPLICIT_HEALTH_CHECK時,將會立刻對問題進行處理。
使用函式為:handleFailureImmediately。
/**
* For native crashes or explicit health check failures, call directly into each observer to
* mitigate the error without going through failure threshold logic.
*/
private void handleFailureImmediately(List<VersionedPackage> packages,
@FailureReasons int failureReason) {
VersionedPackage failingPackage = packages.size() > 0 ? packages.get(0) : null;
PackageHealthObserver currentObserverToNotify = null;
int currentObserverImpact = Integer.MAX_VALUE;
for (ObserverInternal observer: mAllObservers.values()) {
PackageHealthObserver registeredObserver = observer.registeredObserver;
if (registeredObserver != null) {
int impact = registeredObserver.onHealthCheckFailed(
failingPackage, failureReason);
if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
&& impact < currentObserverImpact) {
currentObserverToNotify = registeredObserver;
currentObserverImpact = impact;
}
}
}
if (currentObserverToNotify != null) {
currentObserverToNotify.execute(failingPackage, failureReason);
}
}
在使用後,會執行execute的函式:
@Override
public boolean execute(@Nullable VersionedPackage failedPackage,
@FailureReasons int rollbackReason) {
if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
rollbackAll();
return true;
}
RollbackInfo rollback = getAvailableRollback(failedPackage);
if (rollback == null) {
Slog.w(TAG, "Expected rollback but no valid rollback found for " + failedPackage);
return false;
}
rollbackPackage(rollback, failedPackage, rollbackReason);
// Assume rollback executed successfully
return true;
}
這裡面我們主要關注的是NATIVE_CRASH的實現,所以將會去看rollbackAll的具體實現。
private void rollbackAll() {
Slog.i(TAG, "Rolling back all available rollbacks");
RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks();
// Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all
// pending staged rollbacks are handled.
synchronized (mPendingStagedRollbackIds) {
for (RollbackInfo rollback : rollbacks) {
if (rollback.isStaged()) {
mPendingStagedRollbackIds.add(rollback.getRollbackId());
}
}
}
for (RollbackInfo rollback : rollbacks) {
VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom();
rollbackPackage(rollback, sample, PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
}
}
RollBackPackage具體的實現邏輯如下:
/**
* Rolls back the session that owns {@code failedPackage}
*
* @param rollback {@code rollbackInfo} of the {@code failedPackage}
* @param failedPackage the package that needs to be rolled back
*/
private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage,
@FailureReasons int rollbackReason) {
final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason);
final String failedPackageToLog;
if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
failedPackageToLog = SystemProperties.get(
"sys.init.updatable_crashing_process_name", "");
} else {
failedPackageToLog = failedPackage.getPackageName();
}
VersionedPackage logPackageTemp = null;
if (isModule(failedPackage.getPackageName())) {
logPackageTemp = WatchdogRollbackLogger.getLogPackage(mContext, failedPackage);
}
final VersionedPackage logPackage = logPackageTemp;
WatchdogRollbackLogger.logEvent(logPackage,
FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
reasonToLog, failedPackageToLog);
final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
RollbackManager.STATUS_FAILURE);
if (status == RollbackManager.STATUS_SUCCESS) {
if (rollback.isStaged()) {
int rollbackId = rollback.getRollbackId();
synchronized (mPendingStagedRollbackIds) {
mPendingStagedRollbackIds.add(rollbackId);
}
BroadcastReceiver listener =
listenForStagedSessionReady(rollbackManager, rollbackId,
logPackage);
handleStagedSessionChange(rollbackManager, rollbackId, listener,
logPackage);
} else {
WatchdogRollbackLogger.logEvent(logPackage,
FrameworkStatsLog
.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
reasonToLog, failedPackageToLog);
}
} else {
if (rollback.isStaged()) {
markStagedSessionHandled(rollback.getRollbackId());
}
WatchdogRollbackLogger.logEvent(logPackage,
FrameworkStatsLog
.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
reasonToLog, failedPackageToLog);
}
});
mHandler.post(() ->
rollbackManager.commitRollback(rollback.getRollbackId(),
Collections.singletonList(failedPackage),
rollbackReceiver.getIntentSender()));
}
這裡面我們不去具體的分析某個session,而是回到前文中,提到的具體的狀態,這裡就會看到最後的這麼一個邏輯。
mHandler.post(() ->
rollbackManager.commitRollback(rollback.getRollbackId(),
Collections.singletonList(failedPackage),
rollbackReceiver.getIntentSender()));
這裡面是呼叫了rollbackManager的commitRollback方法:
@Override
public void commitRollback(int rollbackId, ParceledListSlice causePackages,
String callerPackageName, IntentSender statusReceiver) {
enforceManageRollbacks("commitRollback");
final int callingUid = Binder.getCallingUid();
AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
appOps.checkPackage(callingUid, callerPackageName);
getHandler().post(() ->
commitRollbackInternal(rollbackId, causePackages.getList(),
callerPackageName, statusReceiver));
}
其實也就是獲取package,然後去通過commitRollbackInternal處理。
/**
* Performs the actual work to commit a rollback.
* The work is done on the current thread. This may be a long running
* operation.
*/
@WorkerThread
private void commitRollbackInternal(int rollbackId, List<VersionedPackage> causePackages,
String callerPackageName, IntentSender statusReceiver) {
Slog.i(TAG, "commitRollback id=" + rollbackId + " caller=" + callerPackageName);
Rollback rollback = getRollbackForId(rollbackId);
if (rollback == null) {
sendFailure(
mContext, statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
"Rollback unavailable");
return;
}
rollback.commit(mContext, causePackages, callerPackageName, statusReceiver);
}
rollback的commit將會去具體的更改某個rollback的狀態:
/**
* Commits the rollback.
*/
void commit(final Context context, List<VersionedPackage> causePackages,
String callerPackageName, IntentSender statusReceiver) {
synchronized (mLock) {
if (!isAvailable()) {
sendFailure(context, statusReceiver,
RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
"Rollback unavailable");
return;
}
if (containsApex() && wasCreatedAtLowerExtensionVersion()) {
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
if (extensionVersionReductionWouldViolateConstraint(mExtensionVersions, pmi)) {
sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
"Rollback may violate a minExtensionVersion constraint");
return;
}
}
// Get a context to use to install the downgraded version of the package.
Context pkgContext;
try {
pkgContext = context.createPackageContextAsUser(callerPackageName, 0,
UserHandle.of(mUserId));
} catch (PackageManager.NameNotFoundException e) {
sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
"Invalid callerPackageName");
return;
}
PackageManager pm = pkgContext.getPackageManager();
try {
PackageInstaller packageInstaller = pm.getPackageInstaller();
PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
parentParams.setRequestDowngrade(true);
parentParams.setMultiPackage();
if (isStaged()) {
parentParams.setStaged();
}
parentParams.setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK);
int parentSessionId = packageInstaller.createSession(parentParams);
PackageInstaller.Session parentSession = packageInstaller.openSession(
parentSessionId);
for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
if (pkgRollbackInfo.isApkInApex()) {
// No need to issue a downgrade install request for apk-in-apex. It will
// be rolled back when its parent apex is downgraded.
continue;
}
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
String installerPackageName = mInstallerPackageName;
if (TextUtils.isEmpty(mInstallerPackageName)) {
installerPackageName = pm.getInstallerPackageName(
pkgRollbackInfo.getPackageName());
}
if (installerPackageName != null) {
params.setInstallerPackageName(installerPackageName);
}
params.setRequestDowngrade(true);
params.setRequiredInstalledVersionCode(
pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode());
if (isStaged()) {
params.setStaged();
}
if (pkgRollbackInfo.isApex()) {
params.setInstallAsApex();
}
int sessionId = packageInstaller.createSession(params);
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
File[] packageCodePaths = RollbackStore.getPackageCodePaths(
this, pkgRollbackInfo.getPackageName());
if (packageCodePaths == null) {
sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
"Backup copy of package: "
+ pkgRollbackInfo.getPackageName() + " is inaccessible");
return;
}
for (File packageCodePath : packageCodePaths) {
try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
ParcelFileDescriptor.MODE_READ_ONLY)) {
final long token = Binder.clearCallingIdentity();
try {
session.write(packageCodePath.getName(), 0,
packageCodePath.length(),
fd);
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
parentSession.addChildSessionId(sessionId);
}
final LocalIntentReceiver receiver = new LocalIntentReceiver(
(Intent result) -> {
int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
if (status != PackageInstaller.STATUS_SUCCESS) {
// Committing the rollback failed, but we still have all the info we
// need to try rolling back again, so restore the rollback state to
// how it was before we tried committing.
// TODO: Should we just kill this rollback if commit failed?
// Why would we expect commit not to fail again?
// TODO: Could this cause a rollback to be resurrected
// if it should otherwise have expired by now?
synchronized (mLock) {
mState = ROLLBACK_STATE_AVAILABLE;
mRestoreUserDataInProgress = false;
info.setCommittedSessionId(-1);
}
sendFailure(context, statusReceiver,
RollbackManager.STATUS_FAILURE_INSTALL,
"Rollback downgrade install failed: "
+ result.getStringExtra(
PackageInstaller.EXTRA_STATUS_MESSAGE));
return;
}
synchronized (mLock) {
if (!isStaged()) {
// All calls to restoreUserData should have
// completed by now for a non-staged install.
mRestoreUserDataInProgress = false;
}
info.getCausePackages().addAll(causePackages);
RollbackStore.deletePackageCodePaths(this);
RollbackStore.saveRollback(this);
}
// Send success.
try {
final Intent fillIn = new Intent();
fillIn.putExtra(
RollbackManager.EXTRA_STATUS,
RollbackManager.STATUS_SUCCESS);
statusReceiver.sendIntent(context, 0, fillIn, null, null);
} catch (IntentSender.SendIntentException e) {
// Nowhere to send the result back to, so don't bother.
}
Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
for (UserInfo userInfo : UserManager.get(context).getUsers(true)) {
context.sendBroadcastAsUser(broadcast,
userInfo.getUserHandle(),
Manifest.permission.MANAGE_ROLLBACKS);
}
}
);
mState = ROLLBACK_STATE_COMMITTED;
info.setCommittedSessionId(parentSessionId);
mRestoreUserDataInProgress = true;
parentSession.commit(receiver.getIntentSender());
} catch (IOException e) {
Slog.e(TAG, "Rollback failed", e);
sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
"IOException: " + e.toString());
}
}
}
在執行完後,會將mState置為ROLLBACK_STATE_COMMITTED;
mState = ROLLBACK_STATE_COMMITTED;
所以,當我們檢查初始化狀態為
ROLLBACK_STATE_ENABLING,
功能介紹:
在Android 10.0中,Google新增加了個功能。
如果使用者對新升級的APP不滿意,可以通過“回到過去”,回滾到舊版。
當然,如果新安裝的apk出現了各種問題無法使用,也可以進行回滾的操作。
這樣的確可以極大的提升使用者的體驗,但是因為這塊的邏輯較複雜,我們以module_crash_rollback_test為例,來看下具體的實現邏輯。
程式碼路徑如下:
./base/services/core/java/com/android/server/rollback ./base/core/java/android/content/rollback
工作原理:
如何驗證這個功能是否邏輯生效,我們可以使用這個方法:
1. adb install -r -d --enable-rollback --staged ***.apk 2. adb reboot 3. adb shell dumpsys rollback 4. adb root 5. adb shell am crash *** (10 times) 6. adb reboot 7. adb wait-for-devices 1 mins 8. adb shell dumpsys rollback
我們即可從RollBack的狀態,檢查rollback機制是否被啟用以及使用。
dumpsys的code在程式碼中對應如下:程式碼路徑為:frameworks/base/services/core/java/com/android/server/rollback/Rollback.java
void dump(IndentingPrintWriter ipw) { synchronized (mLock) { ipw.println(info.getRollbackId() + ":"); ipw.increaseIndent(); ipw.println("-state: " + getStateAsString()); ipw.println("-timestamp: " + getTimestamp()); if (getStagedSessionId() != -1) { ipw.println("-stagedSessionId: " + getStagedSessionId()); } ipw.println("-packages:"); ipw.increaseIndent(); for (PackageRollbackInfo pkg : info.getPackages()) { ipw.println(pkg.getPackageName() + " " + pkg.getVersionRolledBackFrom().getLongVersionCode() + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode()); } ipw.decreaseIndent(); if (isCommitted()) { ipw.println("-causePackages:"); ipw.increaseIndent(); for (VersionedPackage cPkg : info.getCausePackages()) { ipw.println(cPkg.getPackageName() + " " + cPkg.getLongVersionCode()); } ipw.decreaseIndent(); ipw.println("-committedSessionId: " + info.getCommittedSessionId()); } if (mExtensionVersions.size() > 0) { ipw.println("-extensionVersions:"); ipw.increaseIndent(); ipw.println(mExtensionVersions.toString()); ipw.decreaseIndent(); } ipw.decreaseIndent(); } }
從dumpsys中,我們就可以看到rollback的當前執行狀態。
String getStateAsString() { synchronized (mLock) { return rollbackStateToString(mState); } }
邏輯很簡單,即為將RollBack中的mState變數值置為String並且打出。
變數定義如下:/** * The current state of the rollback. * ENABLING, AVAILABLE, or COMMITTED. */ @GuardedBy("mLock") private @RollbackState int mState;
會有四個狀態值,來對應當前的mState.
@IntDef(prefix = { "ROLLBACK_STATE_" }, value = { ROLLBACK_STATE_ENABLING, ROLLBACK_STATE_AVAILABLE, ROLLBACK_STATE_COMMITTED, ROLLBACK_STATE_DELETED })
那麼在執行module_crash_rollback_test的時候,我們的邏輯是怎麼生效的呢?
首先是在rollbackmanagerservice中:/** * Service that manages APK level rollbacks. Publishes * Context.ROLLBACK_SERVICE. * * @hide */ public final class RollbackManagerService extends SystemService { private RollbackManagerServiceImpl mService; public RollbackManagerService(Context context) { super(context); } @Override public void onStart() { mService = new RollbackManagerServiceImpl(getContext()); publishBinderService(Context.ROLLBACK_SERVICE, mService); } @Override public void onUnlockUser(int user) { mService.onUnlockUser(user); } @Override public void onBootPhase(int phase) { if (phase == SystemService.PHASE_BOOT_COMPLETED) { mService.onBootCompleted(); } }
可以看到的是,在PHASE_BOOT_COMPLETED時,將會呼叫onBootCompleted的函式。
如果看過之前的文章的同學,可能也明白了這個函式是在系統啟動完成後,針對全域性發出的通知。@AnyThread void onBootCompleted() { DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ROLLBACK_BOOT, mExecutor, properties -> updateRollbackLifetimeDurationInMillis()); getHandler().post(() -> { updateRollbackLifetimeDurationInMillis(); runExpiration(); // Check to see if any rollback-enabled staged sessions or staged // rollback sessions been applied. List<Rollback> enabling = new ArrayList<>(); List<Rollback> restoreInProgress = new ArrayList<>(); Set<String> apexPackageNames = new HashSet<>(); synchronized (mLock) { Iterator<Rollback> iter = mRollbacks.iterator(); while (iter.hasNext()) { Rollback rollback = iter.next(); if (!rollback.isStaged()) { // We only care about staged rollbacks here continue; } PackageInstaller.SessionInfo session = mContext.getPackageManager() .getPackageInstaller().getSessionInfo(rollback.getStagedSessionId()); if (session == null || session.isStagedSessionFailed()) { iter.remove(); rollback.delete(mAppDataRollbackHelper); continue; } if (session.isStagedSessionApplied()) { if (rollback.isEnabling()) { enabling.add(rollback); } else if (rollback.isRestoreUserDataInProgress()) { restoreInProgress.add(rollback); } } apexPackageNames.addAll(rollback.getApexPackageNames()); } } for (Rollback rollback : enabling) { makeRollbackAvailable(rollback); } for (Rollback rollback : restoreInProgress) { rollback.setRestoreUserDataInProgress(false); } for (String apexPackageName : apexPackageNames) { // We will not recieve notifications when an apex is updated, // so check now in case any rollbacks ought to be expired. The // onPackagedReplace function is safe to call if the package // hasn't actually been updated. onPackageReplaced(apexPackageName); } synchronized (mLock) { mOrphanedApkSessionIds.clear(); } mPackageHealthObserver.onBootCompletedAsync(); }); }
這段主要說的是在系統啟動過程中,我們將會對rollback的功能開啟,各個session的狀態,以及實際的packageName進行replaced,restore userdata的操作。這邊分析一下onPackageReplaced函式:
/** * Called when a package has been replaced with a different version. * Removes all backups for the package not matching the currently * installed package version. */ @WorkerThread private void onPackageReplaced(String packageName) { // TODO: Could this end up incorrectly deleting a rollback for a // package that is about to be installed? long installedVersion = getInstalledPackageVersion(packageName); synchronized (mLock) { Iterator<Rollback> iter = mRollbacks.iterator(); while (iter.hasNext()) { Rollback rollback = iter.next(); // TODO: Should we remove rollbacks in the ENABLING state here? if ((rollback.isEnabling() || rollback.isAvailable()) && rollback.includesPackageWithDifferentVersion(packageName, installedVersion)) { iter.remove(); rollback.delete(mAppDataRollbackHelper); } } } }
當包被其他版本替換時呼叫時,我們會通過installedVersion來儲存APK的版本號,
並且在下面將會刪除與當前安裝的包版本不匹配的包的所有備份。
在操作完,將會執行onBootCompletedAsync函式,而這邊是進行的通知。/** Verifies the rollback state after a reboot and schedules polling for sometime after reboot * to check for native crashes and mitigate them if needed. */ public void onBootCompletedAsync() { mHandler.post(()->onBootCompleted()); }
那麼這個onBootCompleted在做什麼工作呢?
private void onBootCompleted() { RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); if (!rollbackManager.getAvailableRollbacks().isEmpty()) { // TODO(gavincorkery): Call into Package Watchdog from outside the observer PackageWatchdog.getInstance(mContext).scheduleCheckAndMitigateNativeCrashes(); } SparseArray<String> rollbackIds = popLastStagedRollbackIds(); for (int i = 0; i < rollbackIds.size(); i++) { WatchdogRollbackLogger.logRollbackStatusOnBoot(mContext, rollbackIds.keyAt(i), rollbackIds.valueAt(i), rollbackManager.getRecentlyCommittedRollbacks()); } }
這邊的重頭戲來了,scheduleCheckAndMitigateNativeCrashes看上去和我們要驗證的module_crash_rollback_test非常的相似。
/** * Since this method can eventually trigger a rollback, it should be called * only once boot has completed {@code onBootCompleted} and not earlier, because the install * session must be entirely completed before we try to rollback. */ public void scheduleCheckAndMitigateNativeCrashes() { Slog.i(TAG, "Scheduling " + mNumberOfNativeCrashPollsRemaining + " polls to check " + "and mitigate native crashes"); mShortTaskHandler.post(()->checkAndMitigateNativeCrashes()); }
只是列印了log,就來執行check的操作。
/** * This method should be only called on mShortTaskHandler, since it modifies * {@link #mNumberOfNativeCrashPollsRemaining}. */ private void checkAndMitigateNativeCrashes() { mNumberOfNativeCrashPollsRemaining--; // Check if native watchdog reported a crash if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) { // We rollback everything available when crash is unattributable onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH); // we stop polling after an attempt to execute rollback, regardless of whether the // attempt succeeds or not } else { if (mNumberOfNativeCrashPollsRemaining > 0) { mShortTaskHandler.postDelayed(() -> checkAndMitigateNativeCrashes(), NATIVE_CRASH_POLLING_INTERVAL_MILLIS); } } }
這邊就非常奇怪了,為什麼會有sys.init.updatable_crashing這個systemproperties呢?
這個properties是定義在什麼地方?
程式碼路徑: system/core/init/service.cpp// If we crash > 4 times in 4 minutes or before boot_completed, // reboot into bootloader or set crashing property boot_clock::time_point now = boot_clock::now(); if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART)) { bool boot_completed = android::base::GetBoolProperty("sys.boot_completed", false); if (now < time_crashed_ + 4min || !boot_completed) { if (++crash_count_ > 4) { if (flags_ & SVC_CRITICAL) { // Aborts into bootloader LOG(FATAL) << "critical process '" << name_ << "' exited 4 times " << (boot_completed ? "in 4 minutes" : "before boot completed"); } else { LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times " << (boot_completed ? "in 4 minutes" : "before boot completed"); // Notifies update_verifier and apexd SetProperty("sys.init.updatable_crashing_process_name", name_); SetProperty("sys.init.updatable_crashing", "1"); } } } else { time_crashed_ = now; crash_count_ = 1; } }
這裡其實是對Crash的一個檢查,如果在開機以後,規定時間內有四次以上的crash,然後就會觸發這個properties的定義。
同時會記錄當前程式的名字:sys.init.updatable_crashing_process_name。
但是在正常的過程中,這個應該不會出現。
但是在我們之前測試步驟中,當我們連續crash apk多次,那麼重啟後是否就會啟用rollback呢?
應該是的,我們繼續看看狀態的改變過程。/** * Called when a process fails due to a crash, ANR or explicit health check. * * <p>For each package contained in the process, one registered observer with the least user * impact will be notified for mitigation. * * <p>This method could be called frequently if there is a severe problem on the device. */ public void onPackageFailure(List<VersionedPackage> packages, @FailureReasons int failureReason) { if (packages == null) { Slog.w(TAG, "Could not resolve a list of failing packages"); return; } mLongTaskHandler.post(() -> { synchronized (mLock) { if (mAllObservers.isEmpty()) { return; } boolean requiresImmediateAction = (failureReason == FAILURE_REASON_NATIVE_CRASH || failureReason == FAILURE_REASON_EXPLICIT_HEALTH_CHECK); if (requiresImmediateAction) { handleFailureImmediately(packages, failureReason); } else { for (int pIndex = 0; pIndex < packages.size(); pIndex++) { VersionedPackage versionedPackage = packages.get(pIndex); // Observer that will receive failure for versionedPackage PackageHealthObserver currentObserverToNotify = null; int currentObserverImpact = Integer.MAX_VALUE; // Find observer with least user impact for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { ObserverInternal observer = mAllObservers.valueAt(oIndex); PackageHealthObserver registeredObserver = observer.registeredObserver; if (registeredObserver != null && observer.onPackageFailureLocked( versionedPackage.getPackageName())) { int impact = registeredObserver.onHealthCheckFailed( versionedPackage, failureReason); if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE && impact < currentObserverImpact) { currentObserverToNotify = registeredObserver; currentObserverImpact = impact; } } } // Execute action with least user impact if (currentObserverToNotify != null) { currentObserverToNotify.execute(versionedPackage, failureReason); } } } } }); }
當package failureReason 的原因為Native_Crash和FAILURE_REASON_EXPLICIT_HEALTH_CHECK時,將會立刻對問題進行處理。
使用函式為:handleFailureImmediately。/** * For native crashes or explicit health check failures, call directly into each observer to * mitigate the error without going through failure threshold logic. */ private void handleFailureImmediately(List<VersionedPackage> packages, @FailureReasons int failureReason) { VersionedPackage failingPackage = packages.size() > 0 ? packages.get(0) : null; PackageHealthObserver currentObserverToNotify = null; int currentObserverImpact = Integer.MAX_VALUE; for (ObserverInternal observer: mAllObservers.values()) { PackageHealthObserver registeredObserver = observer.registeredObserver; if (registeredObserver != null) { int impact = registeredObserver.onHealthCheckFailed( failingPackage, failureReason); if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE && impact < currentObserverImpact) { currentObserverToNotify = registeredObserver; currentObserverImpact = impact; } } } if (currentObserverToNotify != null) { currentObserverToNotify.execute(failingPackage, failureReason); } }
在使用後,會執行execute的函式:
@Override public boolean execute(@Nullable VersionedPackage failedPackage, @FailureReasons int rollbackReason) { if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { rollbackAll(); return true; } RollbackInfo rollback = getAvailableRollback(failedPackage); if (rollback == null) { Slog.w(TAG, "Expected rollback but no valid rollback found for " + failedPackage); return false; } rollbackPackage(rollback, failedPackage, rollbackReason); // Assume rollback executed successfully return true; }
這裡面我們主要關注的是NATIVE_CRASH的實現,所以將會去看rollbackAll的具體實現。
private void rollbackAll() { Slog.i(TAG, "Rolling back all available rollbacks"); RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks(); // Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all // pending staged rollbacks are handled. synchronized (mPendingStagedRollbackIds) { for (RollbackInfo rollback : rollbacks) { if (rollback.isStaged()) { mPendingStagedRollbackIds.add(rollback.getRollbackId()); } } } for (RollbackInfo rollback : rollbacks) { VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom(); rollbackPackage(rollback, sample, PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); } }
RollBackPackage具體的實現邏輯如下:
/** * Rolls back the session that owns {@code failedPackage} * * @param rollback {@code rollbackInfo} of the {@code failedPackage} * @param failedPackage the package that needs to be rolled back */ private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage, @FailureReasons int rollbackReason) { final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason); final String failedPackageToLog; if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { failedPackageToLog = SystemProperties.get( "sys.init.updatable_crashing_process_name", ""); } else { failedPackageToLog = failedPackage.getPackageName(); } VersionedPackage logPackageTemp = null; if (isModule(failedPackage.getPackageName())) { logPackageTemp = WatchdogRollbackLogger.getLogPackage(mContext, failedPackage); } final VersionedPackage logPackage = logPackageTemp; WatchdogRollbackLogger.logEvent(logPackage, FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE, reasonToLog, failedPackageToLog); final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> { int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, RollbackManager.STATUS_FAILURE); if (status == RollbackManager.STATUS_SUCCESS) { if (rollback.isStaged()) { int rollbackId = rollback.getRollbackId(); synchronized (mPendingStagedRollbackIds) { mPendingStagedRollbackIds.add(rollbackId); } BroadcastReceiver listener = listenForStagedSessionReady(rollbackManager, rollbackId, logPackage); handleStagedSessionChange(rollbackManager, rollbackId, listener, logPackage); } else { WatchdogRollbackLogger.logEvent(logPackage, FrameworkStatsLog .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, reasonToLog, failedPackageToLog); } } else { if (rollback.isStaged()) { markStagedSessionHandled(rollback.getRollbackId()); } WatchdogRollbackLogger.logEvent(logPackage, FrameworkStatsLog .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, reasonToLog, failedPackageToLog); } }); mHandler.post(() -> rollbackManager.commitRollback(rollback.getRollbackId(), Collections.singletonList(failedPackage), rollbackReceiver.getIntentSender())); }
這裡面我們不去具體的分析某個session,而是回到前文中,提到的具體的狀態,這裡就會看到最後的這麼一個邏輯。
mHandler.post(() -> rollbackManager.commitRollback(rollback.getRollbackId(), Collections.singletonList(failedPackage), rollbackReceiver.getIntentSender()));
這裡面是呼叫了rollbackManager的commitRollback方法:
@Override public void commitRollback(int rollbackId, ParceledListSlice causePackages, String callerPackageName, IntentSender statusReceiver) { enforceManageRollbacks("commitRollback"); final int callingUid = Binder.getCallingUid(); AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); appOps.checkPackage(callingUid, callerPackageName); getHandler().post(() -> commitRollbackInternal(rollbackId, causePackages.getList(), callerPackageName, statusReceiver)); }
其實也就是獲取package,然後去通過commitRollbackInternal處理。
/** * Performs the actual work to commit a rollback. * The work is done on the current thread. This may be a long running * operation. */ @WorkerThread private void commitRollbackInternal(int rollbackId, List<VersionedPackage> causePackages, String callerPackageName, IntentSender statusReceiver) { Slog.i(TAG, "commitRollback id=" + rollbackId + " caller=" + callerPackageName); Rollback rollback = getRollbackForId(rollbackId); if (rollback == null) { sendFailure( mContext, statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE, "Rollback unavailable"); return; } rollback.commit(mContext, causePackages, callerPackageName, statusReceiver); }
rollback的commit將會去具體的更改某個rollback的狀態:
/** * Commits the rollback. */ void commit(final Context context, List<VersionedPackage> causePackages, String callerPackageName, IntentSender statusReceiver) { synchronized (mLock) { if (!isAvailable()) { sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE, "Rollback unavailable"); return; } if (containsApex() && wasCreatedAtLowerExtensionVersion()) { PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); if (extensionVersionReductionWouldViolateConstraint(mExtensionVersions, pmi)) { sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, "Rollback may violate a minExtensionVersion constraint"); return; } } // Get a context to use to install the downgraded version of the package. Context pkgContext; try { pkgContext = context.createPackageContextAsUser(callerPackageName, 0, UserHandle.of(mUserId)); } catch (PackageManager.NameNotFoundException e) { sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, "Invalid callerPackageName"); return; } PackageManager pm = pkgContext.getPackageManager(); try { PackageInstaller packageInstaller = pm.getPackageInstaller(); PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); parentParams.setRequestDowngrade(true); parentParams.setMultiPackage(); if (isStaged()) { parentParams.setStaged(); } parentParams.setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK); int parentSessionId = packageInstaller.createSession(parentParams); PackageInstaller.Session parentSession = packageInstaller.openSession( parentSessionId); for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { if (pkgRollbackInfo.isApkInApex()) { // No need to issue a downgrade install request for apk-in-apex. It will // be rolled back when its parent apex is downgraded. continue; } PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); String installerPackageName = mInstallerPackageName; if (TextUtils.isEmpty(mInstallerPackageName)) { installerPackageName = pm.getInstallerPackageName( pkgRollbackInfo.getPackageName()); } if (installerPackageName != null) { params.setInstallerPackageName(installerPackageName); } params.setRequestDowngrade(true); params.setRequiredInstalledVersionCode( pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode()); if (isStaged()) { params.setStaged(); } if (pkgRollbackInfo.isApex()) { params.setInstallAsApex(); } int sessionId = packageInstaller.createSession(params); PackageInstaller.Session session = packageInstaller.openSession(sessionId); File[] packageCodePaths = RollbackStore.getPackageCodePaths( this, pkgRollbackInfo.getPackageName()); if (packageCodePaths == null) { sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, "Backup copy of package: " + pkgRollbackInfo.getPackageName() + " is inaccessible"); return; } for (File packageCodePath : packageCodePaths) { try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath, ParcelFileDescriptor.MODE_READ_ONLY)) { final long token = Binder.clearCallingIdentity(); try { session.write(packageCodePath.getName(), 0, packageCodePath.length(), fd); } finally { Binder.restoreCallingIdentity(token); } } } parentSession.addChildSessionId(sessionId); } final LocalIntentReceiver receiver = new LocalIntentReceiver( (Intent result) -> { int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); if (status != PackageInstaller.STATUS_SUCCESS) { // Committing the rollback failed, but we still have all the info we // need to try rolling back again, so restore the rollback state to // how it was before we tried committing. // TODO: Should we just kill this rollback if commit failed? // Why would we expect commit not to fail again? // TODO: Could this cause a rollback to be resurrected // if it should otherwise have expired by now? synchronized (mLock) { mState = ROLLBACK_STATE_AVAILABLE; mRestoreUserDataInProgress = false; info.setCommittedSessionId(-1); } sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE_INSTALL, "Rollback downgrade install failed: " + result.getStringExtra( PackageInstaller.EXTRA_STATUS_MESSAGE)); return; } synchronized (mLock) { if (!isStaged()) { // All calls to restoreUserData should have // completed by now for a non-staged install. mRestoreUserDataInProgress = false; } info.getCausePackages().addAll(causePackages); RollbackStore.deletePackageCodePaths(this); RollbackStore.saveRollback(this); } // Send success. try { final Intent fillIn = new Intent(); fillIn.putExtra( RollbackManager.EXTRA_STATUS, RollbackManager.STATUS_SUCCESS); statusReceiver.sendIntent(context, 0, fillIn, null, null); } catch (IntentSender.SendIntentException e) { // Nowhere to send the result back to, so don't bother. } Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED); for (UserInfo userInfo : UserManager.get(context).getUsers(true)) { context.sendBroadcastAsUser(broadcast, userInfo.getUserHandle(), Manifest.permission.MANAGE_ROLLBACKS); } } ); mState = ROLLBACK_STATE_COMMITTED; info.setCommittedSessionId(parentSessionId); mRestoreUserDataInProgress = true; parentSession.commit(receiver.getIntentSender()); } catch (IOException e) { Slog.e(TAG, "Rollback failed", e); sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, "IOException: " + e.toString()); } } }
在執行完後,會將mState置為ROLLBACK_STATE_COMMITTED;
mState = ROLLBACK_STATE_COMMITTED;
所以,當我們檢查初始化狀態為
ROLLBACK_STATE_ENABLING,
開機後的狀態為
ROLLBACK_STATE_AVAILABLE,
執行完crash,rollback後的狀態為
ROLLBACK_STATE_COMMITTED
這個功能的實現和驗證就成功了,實現應用回滾的具體操作。
開機後的狀態為
ROLLBACK_STATE_AVAILABLE,
執行完crash,rollback後的狀態為
ROLLBACK_STATE_COMMITTED
這個功能的實現和驗證就成功了,實現應用回滾的具體操作。
相關文章
- Android 的 Handler 機制實現原理分析Android
- 深度剖析Spring Boot自動裝配機制實現原理Spring Boot
- Android-Handler訊息機制實現原理Android
- Zookeeper watcher 事件機制原理剖析事件
- 深入剖析Linux IO原理和幾種零拷貝機制的實現Linux
- Java反射機制實現與原理Java反射
- Android 原始碼分析 --Handler 機制的實現與工作原理Android原始碼
- Android的IPC機制(二)——AIDL實現原理簡析AndroidAI
- RunTime實現原理剖析
- Android 資源載入機制剖析Android
- Android View 事件傳遞機制剖析AndroidView事件
- javascript事件機制底層實現原理JavaScript事件
- Flink原理與實現:Window機制
- 外掛化實現Android多主題功能原理剖析Android
- 全面剖析Android訊息機制原始碼Android原始碼
- Mysql鎖機制與最佳化實踐以及MVCC底層原理剖析MySqlMVC
- Android10_原理機制系列_事件傳遞機制Android事件
- ARouter原理剖析及手動實現
- 由外到內——剖析Android訊息機制Android
- 利用反射機制實現依賴注入的原理反射依賴注入
- Nestjs模組機制的概念和實現原理JS
- Redis Sentinel實現的機制與原理詳解Redis
- Oracle redo undo commit rollback剖析Oracle RedoMIT
- 微信域名檢測的機制原理以及實現方式
- Fluter訊息機制之微任務實現原理
- Taro cli流程和外掛化機制實現原理
- 基於滴滴雲搭建 S3FS 及其實現機制剖析S3
- Java 併發機制底層實現 —— volatile 原理、synchronize 鎖優化機制Java優化
- Android程式間通訊–訊息機制及IPC機制實現薦Android
- 剖析垃圾回收機制(上)
- Android從原始碼角度剖析View事件分發機制Android原始碼View事件
- 深入理解 Android 訊息機制原理Android
- Netty原始碼解析 -- 事件迴圈機制實現原理Netty原始碼事件
- Angular 依賴注入機制實現原理的深入介紹Angular依賴注入
- 深入詳解Java反射機制與底層實現原理?Java反射
- Kubernetes List-Watch 機制原理與實現 - chunked
- Linux 核心通知鏈機制的原理及實現Linux
- 深入理解Redis主鍵失效原理及實現機制Redis