From 4fc5c86bb9945be49cd77140facf62ba70b5b7e4 Mon Sep 17 00:00:00 2001 From: Joanne Chung Date: Thu, 23 Sep 2021 21:48:31 +0800 Subject: [PATCH] Rebind ContentCaptureService when the binderDied. If the ContentCaptureService has package dependency with other package, if the package is updating, ContentCaptureService will also be killed. In the normal case, if the service will be reconnected but in this case the binding is cleared without any notification to the ContentCaptureManagerService. The bug is in the core platform infra. In the Content Capture code, we only see the binderDied. This change is a short term solution in S, we will rebind the service when the binderDied. To avoid crash loop, we only bind the service with limit rebind counts. Bug: 199609306 Test: manual. Add addPackageDependency to package and update the package. Make sure the state is not zombie. Change-Id: I09a4cf6281a5a259a9a759ca640d6f075726e562 --- .../ContentCaptureManagerService.java | 2 +- .../ContentCapturePerUserService.java | 61 +++++++++++++++++-- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 8a42ddfdc19d1..71749e7871152 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -223,7 +223,7 @@ public final class ContentCaptureManagerService extends @Override // from AbstractMasterSystemService protected ContentCapturePerUserService newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled) { - return new ContentCapturePerUserService(this, mLock, disabled, resolvedUserId); + return new ContentCapturePerUserService(this, mLock, disabled, resolvedUserId, mHandler); } @Override // from SystemService diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 6bd1fa6c335dc..822a42bf50cf2 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -46,6 +46,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; import android.os.Binder; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; import android.provider.Settings; @@ -75,6 +76,7 @@ import com.android.server.contentcapture.RemoteContentCaptureService.ContentCapt import com.android.server.infra.AbstractPerUserSystemService; import java.io.PrintWriter; +import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -88,6 +90,10 @@ final class ContentCapturePerUserService private static final String TAG = ContentCapturePerUserService.class.getSimpleName(); + private static final int MAX_REBIND_COUNTS = 5; + // 5 minutes + private static final long REBIND_DURATION_MS = 5 * 60 * 1_000; + @GuardedBy("mLock") private final SparseArray mSessions = new SparseArray<>(); @@ -121,11 +127,18 @@ final class ContentCapturePerUserService @GuardedBy("mLock") private ContentCaptureServiceInfo mInfo; + private Instant mLastRebindTime; + private int mRebindCount; + private final Handler mHandler; + + private final Runnable mReBindServiceRunnable = new RebindServiceRunnable(); + // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's ContentCapturePerUserService(@NonNull ContentCaptureManagerService master, - @NonNull Object lock, boolean disabled, @UserIdInt int userId) { + @NonNull Object lock, boolean disabled, @UserIdInt int userId, Handler handler) { super(master, lock, userId); + mHandler = handler; updateRemoteServiceLocked(disabled); } @@ -190,9 +203,43 @@ final class ContentCapturePerUserService Slog.w(TAG, "remote service died: " + service); synchronized (mLock) { mZombie = true; - writeServiceEvent( - FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED, - getServiceComponentName()); + // Reset rebindCount if over 12 hours mLastRebindTime + if (mLastRebindTime != null && Instant.now().isAfter( + mLastRebindTime.plusMillis(12 * 60 * 60 * 1000))) { + if (mMaster.debug) { + Slog.i(TAG, "The current rebind count " + mRebindCount + " is reset."); + } + mRebindCount = 0; + } + if (mRebindCount >= MAX_REBIND_COUNTS) { + writeServiceEvent( + FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED, + getServiceComponentName()); + } + if (mRebindCount < MAX_REBIND_COUNTS) { + mHandler.removeCallbacks(mReBindServiceRunnable); + mHandler.postDelayed(mReBindServiceRunnable, REBIND_DURATION_MS); + } + } + } + + private void updateRemoteServiceAndResurrectSessionsLocked() { + boolean disabled = !isEnabledLocked(); + updateRemoteServiceLocked(disabled); + resurrectSessionsLocked(); + } + + private final class RebindServiceRunnable implements Runnable{ + + @Override + public void run() { + synchronized (mLock) { + if (mZombie) { + mLastRebindTime = Instant.now(); + mRebindCount++; + updateRemoteServiceAndResurrectSessionsLocked(); + } + } } } @@ -240,8 +287,8 @@ final class ContentCapturePerUserService } void onPackageUpdatedLocked() { - updateRemoteServiceLocked(!isEnabledLocked()); - resurrectSessionsLocked(); + mRebindCount = 0; + updateRemoteServiceAndResurrectSessionsLocked(); } @GuardedBy("mLock") @@ -555,6 +602,8 @@ final class ContentCapturePerUserService mInfo.dump(prefix2, pw); } pw.print(prefix); pw.print("Zombie: "); pw.println(mZombie); + pw.print(prefix); pw.print("Rebind count: "); pw.println(mRebindCount); + pw.print(prefix); pw.print("Last rebind: "); pw.println(mLastRebindTime); if (mRemoteService != null) { pw.print(prefix); pw.println("remote service:");