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

Commit abbf0876 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Fix up slice service listener management"

parents 960e72e8 9e3b8641
Loading
Loading
Loading
Loading
+41 −11
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import android.app.slice.SliceSpec;
import android.content.ContentProviderClient;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -49,11 +51,13 @@ public class PinnedSliceState {
    @GuardedBy("mLock")
    private final ArraySet<String> mPinnedPkgs = new ArraySet<>();
    @GuardedBy("mLock")
    private final ArraySet<ISliceListener> mListeners = new ArraySet<>();
    private final ArrayMap<IBinder, ISliceListener> mListeners = new ArrayMap<>();
    @GuardedBy("mLock")
    private SliceSpec[] mSupportedSpecs = null;
    @GuardedBy("mLock")
    private final ArrayMap<ISliceListener, String> mPkgMap = new ArrayMap<>();
    private final ArrayMap<IBinder, String> mPkgMap = new ArrayMap<>();

    private final DeathRecipient mDeathRecipient = this::handleRecheckListeners;

    public PinnedSliceState(SliceManagerService service, Uri uri) {
        mService = service;
@@ -107,20 +111,27 @@ public class PinnedSliceState {

    public void addSliceListener(ISliceListener listener, String pkg, SliceSpec[] specs) {
        synchronized (mLock) {
            if (mListeners.add(listener) && mListeners.size() == 1) {
            if (mListeners.size() == 0) {
                mService.listen(mUri);
            }
            mPkgMap.put(listener, pkg);
            try {
                listener.asBinder().linkToDeath(mDeathRecipient, 0);
            } catch (RemoteException e) {
            }
            mListeners.put(listener.asBinder(), listener);
            mPkgMap.put(listener.asBinder(), pkg);
            mergeSpecs(specs);
        }
    }

    public boolean removeSliceListener(ISliceListener listener) {
        synchronized (mLock) {
            mPkgMap.remove(listener);
            if (mListeners.remove(listener) && mListeners.size() == 0) {
            listener.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mPkgMap.remove(listener.asBinder());
            if (mListeners.containsKey(listener.asBinder()) && mListeners.size() == 1) {
                mService.unlisten(mUri);
            }
            mListeners.remove(listener.asBinder());
        }
        return !isPinned();
    }
@@ -159,25 +170,44 @@ public class PinnedSliceState {
        return client;
    }

    private void handleRecheckListeners() {
        if (!isPinned()) return;
        synchronized (mLock) {
            for (int i = mListeners.size() - 1; i >= 0; i--) {
                ISliceListener l = mListeners.valueAt(i);
                if (!l.asBinder().isBinderAlive()) {
                    mListeners.removeAt(i);
                }
            }
            if (!isPinned()) {
                // All the listeners died, remove from pinned state.
                mService.removePinnedSlice(mUri);
            }
        }
    }

    private void handleBind() {
        Slice cachedSlice = doBind(null);
        synchronized (mLock) {
            mListeners.removeIf(l -> {
            if (!isPinned()) return;
            for (int i = mListeners.size() - 1; i >= 0; i--) {
                ISliceListener l = mListeners.valueAt(i);
                Slice s = cachedSlice;
                if (s == null || s.hasHint(Slice.HINT_CALLER_NEEDED)) {
                    s = doBind(mPkgMap.get(l));
                }
                if (s == null) {
                    return true;
                    mListeners.removeAt(i);
                    continue;
                }
                try {
                    l.onSliceUpdated(s);
                    return false;
                } catch (RemoteException e) {
                    Log.e(TAG, "Unable to notify slice " + mUri, e);
                    return true;
                    mListeners.removeAt(i);
                    continue;
                }
            }
            });
            if (!isPinned()) {
                // All the listeners died, remove from pinned state.
                mService.removePinnedSlice(mUri);
+3 −3
Original line number Diff line number Diff line
@@ -91,9 +91,9 @@ public class SliceManagerService extends ISliceManager.Stub {

        mObserver = new ContentObserver(mHandler) {
            @Override
            public void onChange(boolean selfChange, Uri uri) {
            public void onChange(boolean selfChange, Uri uri, int userId) {
                try {
                    getPinnedSlice(uri).onChange();
                    getPinnedSlice(maybeAddUserId(uri, userId)).onChange();
                } catch (IllegalStateException e) {
                    Log.e(TAG, "Received change for unpinned slice " + uri, e);
                }
@@ -204,7 +204,7 @@ public class SliceManagerService extends ISliceManager.Stub {
    }

    ///  ----- internal code -----
    void removePinnedSlice(Uri uri) {
    protected void removePinnedSlice(Uri uri) {
        synchronized (mLock) {
            mPinnedSlicesByUri.remove(uri).destroy();
        }
+33 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
@@ -21,8 +22,11 @@ import android.app.slice.SliceSpec;
import android.content.ContentProvider;
import android.content.IContentProvider;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -34,6 +38,7 @@ import com.android.server.UiServiceTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;

@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -147,6 +152,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
    @Test
    public void testListenerPin() {
        ISliceListener listener = mock(ISliceListener.class);
        when(listener.asBinder()).thenReturn(new Binder());
        assertFalse(mPinnedSliceManager.isPinned());

        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
@@ -159,7 +165,11 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
    @Test
    public void testMultiListenerPin() {
        ISliceListener listener = mock(ISliceListener.class);
        Binder value = new Binder();
        when(listener.asBinder()).thenReturn(value);
        ISliceListener listener2 = mock(ISliceListener.class);
        Binder value2 = new Binder();
        when(listener2.asBinder()).thenReturn(value2);
        assertFalse(mPinnedSliceManager.isPinned());

        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
@@ -171,9 +181,31 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
        assertFalse(mPinnedSliceManager.isPinned());
    }

    @Test
    public void testListenerDeath() throws RemoteException {
        ISliceListener listener = mock(ISliceListener.class);
        IBinder binder = mock(IBinder.class);
        when(binder.isBinderAlive()).thenReturn(true);
        when(listener.asBinder()).thenReturn(binder);
        assertFalse(mPinnedSliceManager.isPinned());

        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
        assertTrue(mPinnedSliceManager.isPinned());

        ArgumentCaptor<DeathRecipient> arg = ArgumentCaptor.forClass(DeathRecipient.class);
        verify(binder).linkToDeath(arg.capture(), anyInt());

        when(binder.isBinderAlive()).thenReturn(false);
        arg.getValue().binderDied();

        verify(mSliceService).removePinnedSlice(eq(TEST_URI));
        assertFalse(mPinnedSliceManager.isPinned());
    }

    @Test
    public void testPkgListenerPin() {
        ISliceListener listener = mock(ISliceListener.class);
        when(listener.asBinder()).thenReturn(new Binder());
        assertFalse(mPinnedSliceManager.isPinned());

        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
@@ -191,6 +223,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
        clearInvocations(mIContentProvider);

        ISliceListener listener = mock(ISliceListener.class);
        when(listener.asBinder()).thenReturn(new Binder());
        Slice s = new Slice.Builder(TEST_URI).build();
        Bundle b = new Bundle();
        b.putParcelable(SliceProvider.EXTRA_SLICE, s);