1 package com.android.server.slice;
3 import static android.testing.TestableContentResolver.UNSTABLE;
5 import static org.junit.Assert.assertArrayEquals;
6 import static org.junit.Assert.assertEquals;
7 import static org.junit.Assert.assertFalse;
8 import static org.junit.Assert.assertNull;
9 import static org.junit.Assert.assertTrue;
10 import static org.mockito.ArgumentMatchers.any;
11 import static org.mockito.ArgumentMatchers.anyInt;
12 import static org.mockito.ArgumentMatchers.anyString;
13 import static org.mockito.ArgumentMatchers.argThat;
14 import static org.mockito.ArgumentMatchers.eq;
15 import static org.mockito.Mockito.doAnswer;
16 import static org.mockito.Mockito.mock;
17 import static org.mockito.Mockito.verify;
18 import static org.mockito.Mockito.when;
20 import android.app.slice.ISliceListener;
21 import android.app.slice.SliceProvider;
22 import android.app.slice.SliceSpec;
23 import android.content.ContentProvider;
24 import android.content.IContentProvider;
25 import android.net.Uri;
26 import android.os.Binder;
27 import android.os.Handler;
28 import android.os.IBinder;
29 import android.os.IBinder.DeathRecipient;
30 import android.os.RemoteException;
31 import android.testing.AndroidTestingRunner;
32 import android.testing.TestableLooper;
33 import android.testing.TestableLooper.RunWithLooper;
35 import androidx.test.filters.SmallTest;
37 import com.android.server.UiServiceTestCase;
39 import org.junit.Before;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 import org.mockito.ArgumentCaptor;
45 @RunWith(AndroidTestingRunner.class)
47 public class PinnedSliceStateTest extends UiServiceTestCase {
49 private static final String AUTH = "my.authority";
50 private static final Uri TEST_URI = Uri.parse("content://" + AUTH + "/path");
52 private static final SliceSpec[] FIRST_SPECS = new SliceSpec[]{
53 new SliceSpec("spec1", 3),
54 new SliceSpec("spec2", 3),
55 new SliceSpec("spec3", 2),
56 new SliceSpec("spec4", 1),
59 private static final SliceSpec[] SECOND_SPECS = new SliceSpec[]{
60 new SliceSpec("spec2", 1),
61 new SliceSpec("spec3", 2),
62 new SliceSpec("spec4", 3),
63 new SliceSpec("spec5", 4),
66 private SliceManagerService mSliceService;
67 private PinnedSliceState mPinnedSliceManager;
68 private IContentProvider mIContentProvider;
69 private ContentProvider mContentProvider;
70 private IBinder mToken = new Binder();
74 mSliceService = mock(SliceManagerService.class);
75 when(mSliceService.getContext()).thenReturn(mContext);
76 when(mSliceService.getLock()).thenReturn(new Object());
77 when(mSliceService.getHandler()).thenReturn(
78 new Handler(TestableLooper.get(this).getLooper()));
79 mContentProvider = mock(ContentProvider.class);
80 mIContentProvider = mock(IContentProvider.class);
81 when(mContentProvider.getIContentProvider()).thenReturn(mIContentProvider);
82 mContext.getContentResolver().addProvider(AUTH, mContentProvider, UNSTABLE);
83 mPinnedSliceManager = new PinnedSliceState(mSliceService, TEST_URI, "pkg");
87 public void testMergeSpecs() {
88 // No annotations to start.
89 assertNull(mPinnedSliceManager.getSpecs());
91 mPinnedSliceManager.mergeSpecs(FIRST_SPECS);
92 assertArrayEquals(FIRST_SPECS, mPinnedSliceManager.getSpecs());
94 mPinnedSliceManager.mergeSpecs(SECOND_SPECS);
95 assertArrayEquals(new SliceSpec[]{
96 // spec1 is gone because it's not in the second set.
97 new SliceSpec("spec2", 1), // spec2 is 1 because it's smaller in the second set.
98 new SliceSpec("spec3", 2), // spec3 is the same in both sets
99 new SliceSpec("spec4", 1), // spec4 is 1 because it's smaller in the first set.
100 // spec5 is gone because it's not in the first set.
101 }, mPinnedSliceManager.getSpecs());
105 public void testSendPinnedOnPin() throws RemoteException {
106 TestableLooper.get(this).processAllMessages();
108 // When pinned for the first time, a pinned message should be sent.
109 mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
110 TestableLooper.get(this).processAllMessages();
112 verify(mIContentProvider).call(anyString(), anyString(), eq(SliceProvider.METHOD_PIN),
113 eq(null), argThat(b -> {
114 assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
120 public void testPkgPin() {
121 assertFalse(mPinnedSliceManager.hasPinOrListener());
123 mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
124 assertTrue(mPinnedSliceManager.hasPinOrListener());
126 assertTrue(mPinnedSliceManager.unpin("pkg", mToken));
127 assertFalse(mPinnedSliceManager.hasPinOrListener());
131 public void testMultiPkgPin() {
132 IBinder t2 = new Binder();
133 assertFalse(mPinnedSliceManager.hasPinOrListener());
135 mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
136 assertTrue(mPinnedSliceManager.hasPinOrListener());
137 mPinnedSliceManager.pin("pkg2", FIRST_SPECS, t2);
139 assertFalse(mPinnedSliceManager.unpin("pkg", mToken));
140 assertTrue(mPinnedSliceManager.unpin("pkg2", t2));
141 assertFalse(mPinnedSliceManager.hasPinOrListener());
145 public void testListenerDeath() throws RemoteException {
146 ISliceListener listener = mock(ISliceListener.class);
147 IBinder binder = mock(IBinder.class);
148 when(binder.isBinderAlive()).thenReturn(true);
149 when(listener.asBinder()).thenReturn(binder);
150 assertFalse(mPinnedSliceManager.hasPinOrListener());
152 mPinnedSliceManager.pin(mContext.getPackageName(), FIRST_SPECS, binder);
153 assertTrue(mPinnedSliceManager.hasPinOrListener());
155 ArgumentCaptor<DeathRecipient> arg = ArgumentCaptor.forClass(DeathRecipient.class);
156 verify(binder).linkToDeath(arg.capture(), anyInt());
158 when(binder.isBinderAlive()).thenReturn(false);
159 arg.getValue().binderDied();
161 verify(mSliceService).removePinnedSlice(eq(TEST_URI));
162 assertFalse(mPinnedSliceManager.hasPinOrListener());
166 public void testPinFailed() throws Exception {
167 // Throw exception when trying to pin
168 doAnswer(invocation -> {
169 throw new Exception("Pin failed");
170 }).when(mIContentProvider).call(
171 anyString(), anyString(), anyString(), eq(null), any());
173 TestableLooper.get(this).processAllMessages();
175 // When pinned for the first time, a pinned message should be sent.
176 mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
177 TestableLooper.get(this).processAllMessages();
179 verify(mIContentProvider).call(anyString(), anyString(), eq(SliceProvider.METHOD_PIN),
180 eq(null), argThat(b -> {
181 assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));