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

Commit c9f0c232 authored by TYM Tsai's avatar TYM Tsai
Browse files

Fix the context leak in the ContentCapture

Activity cannot be released because the context be holded by multiple
objects.
* Activity holds ContentCaptureManager.
* ContentCaptureManager holds the context and MainContextCaptureSession.
* MainContextCaptureSession holds the context and ContentCaptureManager.
* The system server holds some binder references to MainContentCaptureSession.
If the system service never released the binder references, then the
activity is also never GC'd.
To avoid the issue,
1. Make the session state receiver of MainContextCaptureSession to be
   static and uses weak reference to MainContextCaptureSession.
2. The direct service vulture may miss to do unlinkToDeath(), do a checking
   while the session destory.

Bug: 143210612
Test: manual check Objects on the meminfo
Test: Activities should be released after a period of time
Test: adb shell dumpsys meminfo com.google.android.dialer
Change-Id: I12037483addb1efe444c74fa189ef6afd15821dd
parent 4fdd3c8e
Loading
Loading
Loading
Loading
+45 −27
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ import android.view.contentcapture.ViewNode.ViewStructureImpl;
import com.android.internal.os.IResultReceiver;

import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -146,45 +147,57 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
     * Binder object used to update the session state.
     */
    @NonNull
    private final IResultReceiver.Stub mSessionStateReceiver;
    private final SessionStateReceiver mSessionStateReceiver;

    protected MainContentCaptureSession(@NonNull Context context,
            @NonNull ContentCaptureManager manager, @NonNull Handler handler,
            @NonNull IContentCaptureManager systemServerInterface) {
        mContext = context;
        mManager = manager;
        mHandler = handler;
        mSystemServerInterface = systemServerInterface;
    private static class SessionStateReceiver extends IResultReceiver.Stub {
        private final WeakReference<MainContentCaptureSession> mMainSession;

        final int logHistorySize = mManager.mOptions.logHistorySize;
        mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null;
        SessionStateReceiver(MainContentCaptureSession session) {
            mMainSession = new WeakReference<>(session);
        }

        mSessionStateReceiver = new IResultReceiver.Stub() {
        @Override
        public void send(int resultCode, Bundle resultData) {
            final MainContentCaptureSession mainSession = mMainSession.get();
            if (mainSession == null) {
                Log.w(TAG, "received result after mina session released");
                return;
            }
            final IBinder binder;
            if (resultData != null) {
                // Change in content capture enabled.
                final boolean hasEnabled = resultData.getBoolean(EXTRA_ENABLED_STATE);
                if (hasEnabled) {
                    final boolean disabled = (resultCode == RESULT_CODE_FALSE);
                        mDisabled.set(disabled);
                    mainSession.mDisabled.set(disabled);
                    return;
                }
                binder = resultData.getBinder(EXTRA_BINDER);
                if (binder == null) {
                    Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
                        mHandler.post(() -> resetSession(
                    mainSession.mHandler.post(() -> mainSession.resetSession(
                            STATE_DISABLED | STATE_INTERNAL_ERROR));
                    return;
                }
            } else {
                binder = null;
            }
                mHandler.post(() -> onSessionStarted(resultCode, binder));
            mainSession.mHandler.post(() -> mainSession.onSessionStarted(resultCode, binder));
        }
        };
    }

    protected MainContentCaptureSession(@NonNull Context context,
            @NonNull ContentCaptureManager manager, @NonNull Handler handler,
            @NonNull IContentCaptureManager systemServerInterface) {
        mContext = context;
        mManager = manager;
        mHandler = handler;
        mSystemServerInterface = systemServerInterface;

        final int logHistorySize = mManager.mOptions.logHistorySize;
        mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null;

        mSessionStateReceiver = new SessionStateReceiver(this);
    }

    @Override
@@ -543,6 +556,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
            Log.e(TAG, "Error destroying system-service session " + mId + " for "
                    + getDebugState() + ": " + e);
        }

        if (mDirectServiceInterface != null) {
            mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
        }
        mDirectServiceInterface = null;
    }

    // TODO(b/122454205): once we support multiple sessions, we might need to move some of these