Loading services/core/java/com/android/server/slice/PinnedSliceState.java +41 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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(); } Loading Loading @@ -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); Loading services/core/java/com/android/server/slice/SliceManagerService.java +3 −3 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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(); } Loading services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java +33 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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) Loading Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading Loading
services/core/java/com/android/server/slice/PinnedSliceState.java +41 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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(); } Loading Loading @@ -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); Loading
services/core/java/com/android/server/slice/SliceManagerService.java +3 −3 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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(); } Loading
services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java +33 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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) Loading Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading