1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.services.telephony.rcs;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyInt;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.doReturn;
28 import static org.mockito.Mockito.never;
29 import static org.mockito.Mockito.times;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.when;
32 
33 import android.os.Binder;
34 import android.telephony.ims.DelegateRegistrationState;
35 import android.telephony.ims.DelegateRequest;
36 import android.telephony.ims.FeatureTagState;
37 import android.telephony.ims.SipDelegateManager;
38 import android.telephony.ims.aidl.ISipDelegate;
39 import android.telephony.ims.aidl.ISipDelegateMessageCallback;
40 import android.util.ArraySet;
41 
42 import androidx.test.filters.SmallTest;
43 import androidx.test.runner.AndroidJUnit4;
44 
45 import com.android.TelephonyTestBase;
46 import com.android.TestExecutorService;
47 
48 import org.junit.After;
49 import org.junit.Before;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.mockito.ArgumentCaptor;
53 import org.mockito.Captor;
54 import org.mockito.Mock;
55 
56 import java.util.Collections;
57 import java.util.Set;
58 import java.util.concurrent.CompletableFuture;
59 import java.util.concurrent.ScheduledExecutorService;
60 import java.util.function.BiConsumer;
61 import java.util.function.Consumer;
62 import java.util.stream.Collectors;
63 
64 @RunWith(AndroidJUnit4.class)
65 public class SipDelegateControllerTest extends TelephonyTestBase {
66     private static final int TEST_SUB_ID = 1;
67 
68     @Mock private ISipDelegate mMockSipDelegate;
69     @Mock private MessageTransportWrapper mMockMessageTracker;
70     @Mock private ISipDelegateMessageCallback mMockMessageCallback;
71     @Mock private DelegateStateTracker mMockDelegateStateTracker;
72     @Mock private DelegateBinderStateManager mMockBinderConnection;
73     @Captor private ArgumentCaptor<BiConsumer<ISipDelegate, Set<FeatureTagState>>> mCreatedCaptor;
74     @Captor private ArgumentCaptor<Consumer<Boolean>> mBooleanConsumerCaptor;
75     @Captor private ArgumentCaptor<Consumer<Integer>> mIntegerConsumerCaptor;
76 
77     private ScheduledExecutorService mExecutorService;
78 
79     @Before
setUp()80     public void setUp() throws Exception {
81         super.setUp();
82         when(mMockMessageTracker.getMessageCallback()).thenReturn(mMockMessageCallback);
83         mExecutorService = new TestExecutorService();
84     }
85 
86     @After
tearDown()87     public void tearDown() throws Exception {
88         mExecutorService.shutdownNow();
89         super.tearDown();
90     }
91 
92     @SmallTest
93     @Test
testCreateDelegate()94     public void testCreateDelegate() throws Exception {
95         DelegateRequest request = getBaseDelegateRequest();
96         SipDelegateController controller = getTestDelegateController(request,
97                 Collections.emptySet());
98 
99         doReturn(true).when(mMockBinderConnection).create(eq(mMockMessageCallback), any());
100         CompletableFuture<Boolean> future = controller.create(request.getFeatureTags(),
101                 Collections.emptySet() /*denied tags*/);
102         BiConsumer<ISipDelegate, Set<FeatureTagState>> consumer =
103                 verifyConnectionCreated(1);
104         assertNotNull(consumer);
105 
106         assertFalse(future.isDone());
107         consumer.accept(mMockSipDelegate, Collections.emptySet());
108         assertTrue(future.get());
109         verify(mMockMessageTracker).openTransport(mMockSipDelegate, request.getFeatureTags(),
110                 Collections.emptySet());
111         verify(mMockDelegateStateTracker).sipDelegateConnected(request.getFeatureTags(),
112                 Collections.emptySet());
113     }
114 
115     @SmallTest
116     @Test
testCreateDeniedFeatures()117     public void testCreateDeniedFeatures() throws Exception {
118         DelegateRequest request = getLargeDelegateRequest();
119         ArraySet<FeatureTagState> deniedTags = new ArraySet<>(1);
120         deniedTags.add(new FeatureTagState(ImsSignallingUtils.GROUP_CHAT_TAG,
121                 SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
122         SipDelegateController controller = getTestDelegateController(request,
123                 deniedTags);
124 
125         doReturn(true).when(mMockBinderConnection).create(eq(mMockMessageCallback), any());
126         CompletableFuture<Boolean> future = controller.create(request.getFeatureTags(),
127                 deniedTags);
128         BiConsumer<ISipDelegate, Set<FeatureTagState>> consumer =
129                 verifyConnectionCreated(1);
130         assertNotNull(consumer);
131 
132         assertFalse(future.isDone());
133         // Send in additional tags denied by the service
134         deniedTags.add(new FeatureTagState(ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG,
135                 SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
136         consumer.accept(mMockSipDelegate, deniedTags);
137         assertTrue(future.get());
138         // Allowed tags should be initial request set - denied tags
139         ArraySet<String> allowedTags = new ArraySet<>(request.getFeatureTags());
140         allowedTags.removeAll(deniedTags.stream().map(FeatureTagState::getFeatureTag)
141                 .collect(Collectors.toSet()));
142         verify(mMockMessageTracker).openTransport(mMockSipDelegate, allowedTags, deniedTags);
143         verify(mMockDelegateStateTracker).sipDelegateConnected(allowedTags, deniedTags);
144     }
145 
146     @SmallTest
147     @Test
testCreateDelegateTransportDied()148     public void testCreateDelegateTransportDied() throws Exception {
149         DelegateRequest request = getBaseDelegateRequest();
150         SipDelegateController controller = getTestDelegateController(request,
151                 Collections.emptySet());
152 
153         //Create operation fails
154         doReturn(false).when(mMockBinderConnection).create(eq(mMockMessageCallback), any());
155         CompletableFuture<Boolean> future = controller.create(request.getFeatureTags(),
156                 Collections.emptySet() /*denied tags*/);
157 
158         assertFalse(future.get());
159     }
160 
161     @SmallTest
162     @Test
testDestroyDelegate()163     public void testDestroyDelegate() throws Exception {
164         DelegateRequest request = getBaseDelegateRequest();
165         SipDelegateController controller = getTestDelegateController(request,
166                 Collections.emptySet());
167         createSipDelegate(request, controller);
168 
169         CompletableFuture<Integer> pendingDestroy = controller.destroy(false /*force*/,
170                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
171         assertFalse(pendingDestroy.isDone());
172         Consumer<Boolean> pendingClosedConsumer = verifyMessageTrackerCloseGracefully();
173         verify(mMockDelegateStateTracker).sipDelegateChanging(
174                 DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING);
175 
176         // verify we do not call destroy on the delegate until the message tracker releases the
177         // transport.
178         verify(mMockBinderConnection, never()).destroy(anyInt(), any());
179         pendingClosedConsumer.accept(true);
180         Consumer<Integer> pendingDestroyedConsumer = verifyBinderConnectionDestroy();
181         pendingDestroyedConsumer.accept(
182                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
183         verify(mMockDelegateStateTracker).sipDelegateDestroyed(
184                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
185         assertTrue(pendingDestroy.isDone());
186         assertEquals(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP,
187                 pendingDestroy.get().intValue());
188     }
189 
190     @SmallTest
191     @Test
testDestroyDelegateForce()192     public void testDestroyDelegateForce() throws Exception {
193         DelegateRequest request = getBaseDelegateRequest();
194         SipDelegateController controller = getTestDelegateController(request,
195                 Collections.emptySet());
196         createSipDelegate(request, controller);
197 
198         CompletableFuture<Integer> pendingDestroy = controller.destroy(true /*force*/,
199                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
200         assertFalse(pendingDestroy.isDone());
201         // Do not wait for message transport close in this case.
202         verify(mMockMessageTracker).close(
203                 SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
204         verify(mMockDelegateStateTracker, never()).sipDelegateChanging(
205                 DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING);
206 
207         //verify destroy is called
208         Consumer<Integer> pendingDestroyedConsumer = verifyBinderConnectionDestroy();
209         pendingDestroyedConsumer.accept(
210                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
211         verify(mMockDelegateStateTracker).sipDelegateDestroyed(
212                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
213         assertTrue(pendingDestroy.isDone());
214         assertEquals(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP,
215                 pendingDestroy.get().intValue());
216     }
217 
218     @SmallTest
219     @Test
testChangeSupportedFeatures()220     public void testChangeSupportedFeatures() throws Exception {
221         DelegateRequest request = getBaseDelegateRequest();
222         SipDelegateController controller = getTestDelegateController(request,
223                 Collections.emptySet());
224         createSipDelegate(request, controller);
225 
226         Set<String> newFts = getBaseFTSet();
227         newFts.add(ImsSignallingUtils.GROUP_CHAT_TAG);
228         CompletableFuture<Boolean> pendingChange = controller.changeSupportedFeatureTags(
229                 newFts, Collections.emptySet());
230         assertFalse(pendingChange.isDone());
231         // message tracker should close gracefully.
232         Consumer<Boolean> pendingClosedConsumer = verifyMessageTrackerCloseGracefully();
233         verify(mMockDelegateStateTracker).sipDelegateChanging(
234                 DelegateRegistrationState.DEREGISTERING_REASON_FEATURE_TAGS_CHANGING);
235         verify(mMockBinderConnection, never()).destroy(anyInt(), any());
236         pendingClosedConsumer.accept(true);
237         Consumer<Integer> pendingDestroyedConsumer = verifyBinderConnectionDestroy();
238         pendingDestroyedConsumer.accept(
239                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
240         verify(mMockDelegateStateTracker, never()).sipDelegateDestroyed(anyInt());
241 
242         // This will cause any exceptions to be printed if something completed exceptionally.
243         assertNull(pendingChange.getNow(null));
244         BiConsumer<ISipDelegate, Set<FeatureTagState>> consumer =
245                 verifyConnectionCreated(2);
246         assertNotNull(consumer);
247         consumer.accept(mMockSipDelegate, Collections.emptySet());
248         assertTrue(pendingChange.get());
249 
250         verify(mMockMessageTracker).openTransport(mMockSipDelegate, request.getFeatureTags(),
251                 Collections.emptySet());
252         verify(mMockMessageTracker).openTransport(mMockSipDelegate, newFts,
253                 Collections.emptySet());
254         verify(mMockDelegateStateTracker).sipDelegateConnected(
255                 request.getFeatureTags(), Collections.emptySet());
256         verify(mMockDelegateStateTracker).sipDelegateConnected(newFts, Collections.emptySet());
257     }
258 
createSipDelegate(DelegateRequest request, SipDelegateController controller)259     private void createSipDelegate(DelegateRequest request, SipDelegateController controller)
260             throws Exception {
261         doReturn(true).when(mMockBinderConnection).create(eq(mMockMessageCallback), any());
262         CompletableFuture<Boolean> future = controller.create(request.getFeatureTags(),
263                 Collections.emptySet() /*denied tags*/);
264         BiConsumer<ISipDelegate, Set<FeatureTagState>> consumer =
265                 verifyConnectionCreated(1);
266         assertNotNull(consumer);
267         consumer.accept(mMockSipDelegate, Collections.emptySet());
268         assertTrue(future.get());
269     }
270 
getBaseFTSet()271     private ArraySet<String> getBaseFTSet() {
272         ArraySet<String> request = new ArraySet<>();
273         request.add(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG);
274         return request;
275     }
276 
getLargeFTSet()277     private ArraySet<String> getLargeFTSet() {
278         ArraySet<String> request = new ArraySet<>();
279         request.add(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG);
280         request.add(ImsSignallingUtils.GROUP_CHAT_TAG);
281         request.add(ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG);
282         return request;
283     }
284 
getBaseDelegateRequest()285     private DelegateRequest getBaseDelegateRequest() {
286         return new DelegateRequest(getBaseFTSet());
287     }
288 
getLargeDelegateRequest()289     private DelegateRequest getLargeDelegateRequest() {
290         return new DelegateRequest(getLargeFTSet());
291     }
292 
getTestDelegateController(DelegateRequest request, Set<FeatureTagState> deniedSet)293     private SipDelegateController getTestDelegateController(DelegateRequest request,
294             Set<FeatureTagState> deniedSet) {
295         return new SipDelegateController(TEST_SUB_ID, Binder.getCallingUid(), request, "",
296                 mExecutorService, mMockMessageTracker, mMockDelegateStateTracker,
297                 (a, b, deniedFeatureSet, d, e) ->  {
298                     assertEquals(deniedSet, deniedFeatureSet);
299                     return mMockBinderConnection;
300                 });
301     }
302 
verifyConnectionCreated(int numTimes)303     private BiConsumer<ISipDelegate, Set<FeatureTagState>> verifyConnectionCreated(int numTimes) {
304         verify(mMockBinderConnection, times(numTimes)).create(eq(mMockMessageCallback),
305                 mCreatedCaptor.capture());
306         return mCreatedCaptor.getValue();
307     }
308 
verifyMessageTrackerCloseGracefully()309     private Consumer<Boolean> verifyMessageTrackerCloseGracefully() {
310         verify(mMockMessageTracker).closeGracefully(anyInt(), anyInt(),
311                 mBooleanConsumerCaptor.capture());
312         return mBooleanConsumerCaptor.getValue();
313     }
verifyBinderConnectionDestroy()314     private Consumer<Integer> verifyBinderConnectionDestroy() {
315         verify(mMockBinderConnection).destroy(anyInt(), mIntegerConsumerCaptor.capture());
316         return mIntegerConsumerCaptor.getValue();
317     }
318 
319 }
320