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

Commit a5c2f22f authored by Jaewan Kim's avatar Jaewan Kim
Browse files

Remove strong references in ServiceStub

Binder stub implement isn't GCed while any other process is
holding it. Better not keeping the strong references.

Bug: 146030676
Test: Run CtsMediaTestCases
Change-Id: I9c95370e23ed6ad76d00689223c1b74c669f5a9d
parent 18c9b13c
Loading
Loading
Loading
Loading
+85 −41
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -112,21 +113,34 @@ public abstract class MediaBrowserService extends Service {
    /**
     * All the info about a connection.
     */
    private class ConnectionRecord implements IBinder.DeathRecipient {
        String pkg;
        int uid;
        int pid;
        Bundle rootHints;
        IMediaBrowserServiceCallbacks callbacks;
        BrowserRoot root;
        HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
    private static class ConnectionRecord implements IBinder.DeathRecipient {
        public final MediaBrowserService service;
        public final String pkg;
        public final int pid;
        public final int uid;
        public final Bundle rootHints;
        public final IMediaBrowserServiceCallbacks callbacks;
        public final BrowserRoot root;
        public final HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();

        ConnectionRecord(
                MediaBrowserService service, String pkg, int pid, int uid, Bundle rootHints,
                IMediaBrowserServiceCallbacks callbacks, BrowserRoot root) {
            this.service = service;
            this.pkg = pkg;
            this.pid = pid;
            this.uid = uid;
            this.rootHints = rootHints;
            this.callbacks = callbacks;
            this.root = root;
        }

        @Override
        public void binderDied() {
            mHandler.post(new Runnable() {
            service.mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mConnections.remove(callbacks.asBinder());
                    service.mConnections.remove(callbacks.asBinder());
                }
            });
        }
@@ -199,39 +213,46 @@ public abstract class MediaBrowserService extends Service {
        }
    }

    private class ServiceBinder extends IMediaBrowserService.Stub {
    private static class ServiceBinder extends IMediaBrowserService.Stub {
        private WeakReference<MediaBrowserService> mService;

        private ServiceBinder(MediaBrowserService service) {
            mService = new WeakReference(service);
        }

        @Override
        public void connect(final String pkg, final Bundle rootHints,
                final IMediaBrowserServiceCallbacks callbacks) {
            MediaBrowserService service = mService.get();
            if (service == null) {
                return;
            }

            final int pid = Binder.getCallingPid();
            final int uid = Binder.getCallingUid();
            if (!isValidPackage(pkg, uid)) {
            if (!service.isValidPackage(pkg, uid)) {
                throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
                        + " package=" + pkg);
            }

            mHandler.post(new Runnable() {
            service.mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        final IBinder b = callbacks.asBinder();

                        // Clear out the old subscriptions. We are getting new ones.
                        mConnections.remove(b);

                        final ConnectionRecord connection = new ConnectionRecord();
                        connection.pkg = pkg;
                        connection.pid = pid;
                        connection.uid = uid;
                        connection.rootHints = rootHints;
                        connection.callbacks = callbacks;
                        service.mConnections.remove(b);

                        mCurConnection = connection;
                        connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
                        mCurConnection = null;
                        // Temporarily sets a placeholder ConnectionRecord to make
                        // getCurrentBrowserInfo() work in onGetRoot().
                        service.mCurConnection =
                                new ConnectionRecord(
                                        service, pkg, pid, uid, rootHints, callbacks, null);
                        BrowserRoot root = service.onGetRoot(pkg, uid, rootHints);
                        service.mCurConnection = null;

                        // If they didn't return something, don't allow this client.
                        if (connection.root == null) {
                        if (root == null) {
                            Log.i(TAG, "No root for client " + pkg + " from service "
                                    + getClass().getName());
                            try {
@@ -242,16 +263,19 @@ public abstract class MediaBrowserService extends Service {
                            }
                        } else {
                            try {
                                mConnections.put(b, connection);
                                ConnectionRecord connection =
                                        new ConnectionRecord(
                                                service, pkg, pid, uid, rootHints, callbacks, root);
                                service.mConnections.put(b, connection);
                                b.linkToDeath(connection, 0);
                                if (mSession != null) {
                                if (service.mSession != null) {
                                    callbacks.onConnect(connection.root.getRootId(),
                                            mSession, connection.root.getExtras());
                                            service.mSession, connection.root.getExtras());
                                }
                            } catch (RemoteException ex) {
                                Log.w(TAG, "Calling onConnect() failed. Dropping client. "
                                        + "pkg=" + pkg);
                                mConnections.remove(b);
                                service.mConnections.remove(b);
                            }
                        }
                    }
@@ -260,13 +284,18 @@ public abstract class MediaBrowserService extends Service {

        @Override
        public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
            mHandler.post(new Runnable() {
            MediaBrowserService service = mService.get();
            if (service == null) {
                return;
            }

            service.mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        final IBinder b = callbacks.asBinder();

                        // Clear out the old subscriptions. We are getting new ones.
                        final ConnectionRecord old = mConnections.remove(b);
                        final ConnectionRecord old = service.mConnections.remove(b);
                        if (old != null) {
                            // TODO
                            old.callbacks.asBinder().unlinkToDeath(old, 0);
@@ -283,20 +312,25 @@ public abstract class MediaBrowserService extends Service {
        @Override
        public void addSubscription(final String id, final IBinder token, final Bundle options,
                final IMediaBrowserServiceCallbacks callbacks) {
            mHandler.post(new Runnable() {
            MediaBrowserService service = mService.get();
            if (service == null) {
                return;
            }

            service.mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        final IBinder b = callbacks.asBinder();

                        // Get the record for the connection
                        final ConnectionRecord connection = mConnections.get(b);
                        final ConnectionRecord connection = service.mConnections.get(b);
                        if (connection == null) {
                            Log.w(TAG, "addSubscription for callback that isn't registered id="
                                    + id);
                            return;
                        }

                        MediaBrowserService.this.addSubscription(id, connection, token, options);
                        service.addSubscription(id, connection, token, options);
                    }
                });
        }
@@ -310,18 +344,23 @@ public abstract class MediaBrowserService extends Service {
        @Override
        public void removeSubscription(final String id, final IBinder token,
                final IMediaBrowserServiceCallbacks callbacks) {
            mHandler.post(new Runnable() {
            MediaBrowserService service = mService.get();
            if (service == null) {
                return;
            }

            service.mHandler.post(new Runnable() {
                @Override
                public void run() {
                    final IBinder b = callbacks.asBinder();

                    ConnectionRecord connection = mConnections.get(b);
                    ConnectionRecord connection = service.mConnections.get(b);
                    if (connection == null) {
                        Log.w(TAG, "removeSubscription for callback that isn't registered id="
                                + id);
                        return;
                    }
                    if (!MediaBrowserService.this.removeSubscription(id, connection, token)) {
                    if (!service.removeSubscription(id, connection, token)) {
                        Log.w(TAG, "removeSubscription called for " + id
                                + " which is not subscribed");
                    }
@@ -332,16 +371,21 @@ public abstract class MediaBrowserService extends Service {
        @Override
        public void getMediaItem(final String mediaId, final ResultReceiver receiver,
                final IMediaBrowserServiceCallbacks callbacks) {
            mHandler.post(new Runnable() {
            MediaBrowserService service = mService.get();
            if (service == null) {
                return;
            }

            service.mHandler.post(new Runnable() {
                @Override
                public void run() {
                    final IBinder b = callbacks.asBinder();
                    ConnectionRecord connection = mConnections.get(b);
                    ConnectionRecord connection = service.mConnections.get(b);
                    if (connection == null) {
                        Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
                        return;
                    }
                    performLoadItem(mediaId, connection, receiver);
                    service.performLoadItem(mediaId, connection, receiver);
                }
            });
        }
@@ -350,7 +394,7 @@ public abstract class MediaBrowserService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        mBinder = new ServiceBinder();
        mBinder = new ServiceBinder(this);
    }

    @Override