Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4fc5c86b authored by Joanne Chung's avatar Joanne Chung
Browse files

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
parent 83616704
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+55 −6
Original line number Diff line number Diff line
@@ -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<ContentCaptureServerSession> 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,10 +203,44 @@ final class ContentCapturePerUserService
        Slog.w(TAG, "remote service died: " + service);
        synchronized (mLock) {
            mZombie = true;
            // 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:");