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