| package com.android.server.slice; |
| |
| import static android.testing.TestableContentResolver.UNSTABLE; |
| |
| import static org.junit.Assert.assertArrayEquals; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.anyString; |
| import static org.mockito.ArgumentMatchers.argThat; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import android.app.slice.ISliceListener; |
| import android.app.slice.SliceProvider; |
| import android.app.slice.SliceSpec; |
| import android.content.ContentProvider; |
| import android.content.IContentProvider; |
| import android.net.Uri; |
| import android.os.Binder; |
| 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; |
| import android.testing.TestableLooper; |
| import android.testing.TestableLooper.RunWithLooper; |
| |
| 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) |
| @RunWithLooper |
| public class PinnedSliceStateTest extends UiServiceTestCase { |
| |
| private static final String AUTH = "my.authority"; |
| private static final Uri TEST_URI = Uri.parse("content://" + AUTH + "/path"); |
| |
| private static final SliceSpec[] FIRST_SPECS = new SliceSpec[]{ |
| new SliceSpec("spec1", 3), |
| new SliceSpec("spec2", 3), |
| new SliceSpec("spec3", 2), |
| new SliceSpec("spec4", 1), |
| }; |
| |
| private static final SliceSpec[] SECOND_SPECS = new SliceSpec[]{ |
| new SliceSpec("spec2", 1), |
| new SliceSpec("spec3", 2), |
| new SliceSpec("spec4", 3), |
| new SliceSpec("spec5", 4), |
| }; |
| |
| private SliceManagerService mSliceService; |
| private PinnedSliceState mPinnedSliceManager; |
| private IContentProvider mIContentProvider; |
| private ContentProvider mContentProvider; |
| private IBinder mToken = new Binder(); |
| |
| @Before |
| public void setup() { |
| mSliceService = mock(SliceManagerService.class); |
| when(mSliceService.getContext()).thenReturn(mContext); |
| when(mSliceService.getLock()).thenReturn(new Object()); |
| when(mSliceService.getHandler()).thenReturn( |
| new Handler(TestableLooper.get(this).getLooper())); |
| mContentProvider = mock(ContentProvider.class); |
| mIContentProvider = mock(IContentProvider.class); |
| when(mContentProvider.getIContentProvider()).thenReturn(mIContentProvider); |
| mContext.getContentResolver().addProvider(AUTH, mContentProvider, UNSTABLE); |
| mPinnedSliceManager = new PinnedSliceState(mSliceService, TEST_URI, "pkg"); |
| } |
| |
| @Test |
| public void testMergeSpecs() { |
| // No annotations to start. |
| assertNull(mPinnedSliceManager.getSpecs()); |
| |
| mPinnedSliceManager.mergeSpecs(FIRST_SPECS); |
| assertArrayEquals(FIRST_SPECS, mPinnedSliceManager.getSpecs()); |
| |
| mPinnedSliceManager.mergeSpecs(SECOND_SPECS); |
| assertArrayEquals(new SliceSpec[]{ |
| // spec1 is gone because it's not in the second set. |
| new SliceSpec("spec2", 1), // spec2 is 1 because it's smaller in the second set. |
| new SliceSpec("spec3", 2), // spec3 is the same in both sets |
| new SliceSpec("spec4", 1), // spec4 is 1 because it's smaller in the first set. |
| // spec5 is gone because it's not in the first set. |
| }, mPinnedSliceManager.getSpecs()); |
| } |
| |
| @Test |
| public void testSendPinnedOnPin() throws RemoteException { |
| TestableLooper.get(this).processAllMessages(); |
| |
| // When pinned for the first time, a pinned message should be sent. |
| mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken); |
| TestableLooper.get(this).processAllMessages(); |
| |
| verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null), |
| argThat(b -> { |
| assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI)); |
| return true; |
| })); |
| } |
| |
| @Test |
| public void testPkgPin() { |
| assertFalse(mPinnedSliceManager.hasPinOrListener()); |
| |
| mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken); |
| assertTrue(mPinnedSliceManager.hasPinOrListener()); |
| |
| assertTrue(mPinnedSliceManager.unpin("pkg", mToken)); |
| assertFalse(mPinnedSliceManager.hasPinOrListener()); |
| } |
| |
| @Test |
| public void testMultiPkgPin() { |
| IBinder t2 = new Binder(); |
| assertFalse(mPinnedSliceManager.hasPinOrListener()); |
| |
| mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken); |
| assertTrue(mPinnedSliceManager.hasPinOrListener()); |
| mPinnedSliceManager.pin("pkg2", FIRST_SPECS, t2); |
| |
| assertFalse(mPinnedSliceManager.unpin("pkg", mToken)); |
| assertTrue(mPinnedSliceManager.unpin("pkg2", t2)); |
| assertFalse(mPinnedSliceManager.hasPinOrListener()); |
| } |
| |
| @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.hasPinOrListener()); |
| |
| mPinnedSliceManager.pin(mContext.getPackageName(), FIRST_SPECS, binder); |
| assertTrue(mPinnedSliceManager.hasPinOrListener()); |
| |
| 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.hasPinOrListener()); |
| } |
| } |