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.assertNotNull; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.fail; 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.ArgumentMatchers.anyInt; 25 import static org.mockito.ArgumentMatchers.eq; 26 import static org.mockito.Mockito.doReturn; 27 import static org.mockito.Mockito.mock; 28 import static org.mockito.Mockito.never; 29 import static org.mockito.Mockito.times; 30 import static org.mockito.Mockito.verify; 31 32 import android.telephony.AccessNetworkConstants; 33 import android.telephony.ims.ImsException; 34 import android.telephony.ims.ImsReasonInfo; 35 import android.telephony.ims.ImsRegistrationAttributes; 36 import android.telephony.ims.RegistrationManager; 37 import android.telephony.ims.aidl.IImsCapabilityCallback; 38 import android.telephony.ims.aidl.IImsRegistrationCallback; 39 import android.telephony.ims.feature.RcsFeature; 40 import android.telephony.ims.stub.ImsRegistrationImplBase; 41 42 import androidx.test.runner.AndroidJUnit4; 43 44 import com.android.TelephonyTestBase; 45 import com.android.ims.FeatureConnector; 46 import com.android.ims.RcsFeatureManager; 47 import com.android.internal.telephony.imsphone.ImsRegistrationCallbackHelper; 48 49 import org.junit.After; 50 import org.junit.Before; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 import org.mockito.ArgumentCaptor; 54 import org.mockito.Captor; 55 import org.mockito.Mock; 56 57 import java.util.concurrent.Executor; 58 59 @RunWith(AndroidJUnit4.class) 60 public class RcsFeatureControllerTest extends TelephonyTestBase { 61 62 private static final int TEST_SUB_ID = 1; 63 64 private static final ImsReasonInfo REASON_DISCONNECTED = new ImsReasonInfo( 65 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN, 0, "test"); 66 67 @Mock RcsFeatureManager mFeatureManager; 68 @Mock RcsFeatureController.FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory; 69 @Mock ImsRegistrationCallbackHelper.ImsRegistrationUpdate mRegistrationCallback; 70 @Mock FeatureConnector<RcsFeatureManager> mFeatureConnector; 71 @Mock RcsFeatureController.Feature mMockFeature; 72 @Captor ArgumentCaptor<FeatureConnector.Listener<RcsFeatureManager>> mConnectorListener; 73 74 private RcsFeatureController.RegistrationHelperFactory mRegistrationFactory = 75 new RcsFeatureController.RegistrationHelperFactory() { 76 @Override 77 public ImsRegistrationCallbackHelper create( 78 ImsRegistrationCallbackHelper.ImsRegistrationUpdate cb, Executor executor) { 79 // Run on current thread for testing. 80 return new ImsRegistrationCallbackHelper(mRegistrationCallback, Runnable::run); 81 } 82 }; 83 84 @Before setUp()85 public void setUp() throws Exception { 86 super.setUp(); 87 } 88 89 @After tearDown()90 public void tearDown() throws Exception { 91 super.tearDown(); 92 } 93 94 @Test testRcsFeatureManagerConnectDisconnect()95 public void testRcsFeatureManagerConnectDisconnect() throws Exception { 96 RcsFeatureController controller = createFeatureController(); 97 controller.addFeature(mMockFeature, RcsFeatureController.Feature.class); 98 verify(mMockFeature).onRcsDisconnected(); 99 // Connect the RcsFeatureManager 100 mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID); 101 102 verify(mFeatureManager).updateCapabilities(TEST_SUB_ID); 103 verify(mFeatureManager).registerImsRegistrationCallback(any()); 104 verify(mMockFeature).onRcsConnected(mFeatureManager); 105 106 // Disconnect 107 mConnectorListener.getValue().connectionUnavailable( 108 FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED); 109 110 verify(mFeatureManager).unregisterImsRegistrationCallback(any()); 111 verify(mMockFeature, times(2)).onRcsDisconnected(); 112 } 113 114 @Test testFeatureManagerConnectedAddRemoveFeature()115 public void testFeatureManagerConnectedAddRemoveFeature() throws Exception { 116 RcsFeatureController controller = createFeatureController(); 117 // Connect the RcsFeatureManager 118 mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID); 119 controller.addFeature(mMockFeature, RcsFeatureController.Feature.class); 120 121 verify(mMockFeature).onRcsConnected(mFeatureManager); 122 assertEquals(mMockFeature, controller.getFeature(RcsFeatureController.Feature.class)); 123 124 controller.removeFeature(RcsFeatureController.Feature.class); 125 verify(mMockFeature).onDestroy(); 126 assertNull(controller.getFeature(RcsFeatureController.Feature.class)); 127 } 128 129 @Test testFeatureManagerConnectedRegister()130 public void testFeatureManagerConnectedRegister() throws Exception { 131 RcsFeatureController controller = createFeatureController(); 132 IImsRegistrationCallback regCb = mock(IImsRegistrationCallback.class); 133 IImsCapabilityCallback capCb = mock(IImsCapabilityCallback.class); 134 // Connect the RcsFeatureManager 135 mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID); 136 137 try { 138 controller.registerImsRegistrationCallback(TEST_SUB_ID, regCb); 139 controller.registerRcsAvailabilityCallback(TEST_SUB_ID, capCb); 140 controller.isCapable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE, 141 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 142 controller.isAvailable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE, 143 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 144 controller.getRegistrationTech(integer -> { 145 }); 146 verify(mFeatureManager).registerImsRegistrationCallback(TEST_SUB_ID, regCb); 147 verify(mFeatureManager).registerRcsAvailabilityCallback(TEST_SUB_ID, capCb); 148 verify(mFeatureManager).isCapable( 149 RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE, 150 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 151 verify(mFeatureManager).isAvailable( 152 RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE, 153 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 154 verify(mFeatureManager).getImsRegistrationTech(any()); 155 } catch (ImsException e) { 156 fail("ImsException not expected."); 157 } 158 159 controller.unregisterImsRegistrationCallback(TEST_SUB_ID, regCb); 160 controller.unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb); 161 verify(mFeatureManager).unregisterImsRegistrationCallback(TEST_SUB_ID, regCb); 162 verify(mFeatureManager).unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb); 163 } 164 165 @Test testFeatureManagerConnectedHelper()166 public void testFeatureManagerConnectedHelper() throws Exception { 167 RcsFeatureController controller = createFeatureController(); 168 // Connect the RcsFeatureManager 169 mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID); 170 ArgumentCaptor<IImsRegistrationCallback> captor = 171 ArgumentCaptor.forClass(IImsRegistrationCallback.class); 172 verify(mFeatureManager).registerImsRegistrationCallback(captor.capture()); 173 assertNotNull(captor.getValue()); 174 175 captor.getValue().onDeregistered(REASON_DISCONNECTED, 0, 0); 176 controller.getRegistrationState(result -> { 177 assertNotNull(result); 178 assertEquals(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED, result.intValue()); 179 }); 180 verify(mRegistrationCallback).handleImsUnregistered(eq(REASON_DISCONNECTED), 181 anyInt(), anyInt()); 182 183 ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder( 184 ImsRegistrationImplBase.REGISTRATION_TECH_LTE).build(); 185 captor.getValue().onRegistering(attr); 186 controller.getRegistrationState(result -> { 187 assertNotNull(result); 188 assertEquals(RegistrationManager.REGISTRATION_STATE_REGISTERING, result.intValue()); 189 }); 190 verify(mRegistrationCallback).handleImsRegistering( 191 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 192 193 captor.getValue().onRegistered(attr); 194 controller.getRegistrationState(result -> { 195 assertNotNull(result); 196 assertEquals(RegistrationManager.REGISTRATION_STATE_REGISTERED, result.intValue()); 197 }); 198 verify(mRegistrationCallback).handleImsRegistered(attr); 199 } 200 201 @Test testFeatureManagerDisconnectedAddFeature()202 public void testFeatureManagerDisconnectedAddFeature() { 203 RcsFeatureController controller = createFeatureController(); 204 // Disconnect the RcsFeatureManager 205 mConnectorListener.getValue().connectionUnavailable( 206 FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED); 207 controller.addFeature(mMockFeature, RcsFeatureController.Feature.class); 208 209 verify(mMockFeature).onRcsDisconnected(); 210 } 211 212 @Test testFeatureManagerDisconnectedException()213 public void testFeatureManagerDisconnectedException() { 214 RcsFeatureController controller = createFeatureController(); 215 IImsRegistrationCallback regCb = mock(IImsRegistrationCallback.class); 216 IImsCapabilityCallback capCb = mock(IImsCapabilityCallback.class); 217 // Disconnect the RcsFeatureManager 218 mConnectorListener.getValue().connectionUnavailable( 219 FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED); 220 221 try { 222 controller.registerImsRegistrationCallback(TEST_SUB_ID, null /*callback*/); 223 fail("ImsException expected for IMS registration."); 224 } catch (ImsException e) { 225 //expected 226 } 227 try { 228 controller.registerRcsAvailabilityCallback(TEST_SUB_ID, null /*callback*/); 229 fail("ImsException expected for availability"); 230 } catch (ImsException e) { 231 //expected 232 } 233 try { 234 controller.isCapable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE, 235 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 236 fail("ImsException expected for capability check"); 237 } catch (ImsException e) { 238 //expected 239 } 240 try { 241 controller.isAvailable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE, 242 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 243 fail("ImsException expected for availability check"); 244 } catch (ImsException e) { 245 //expected 246 } 247 controller.getRegistrationTech(integer -> { 248 assertNotNull(integer); 249 assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, integer.intValue()); 250 }); 251 controller.unregisterImsRegistrationCallback(TEST_SUB_ID, regCb); 252 controller.unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb); 253 verify(mFeatureManager, never()).unregisterImsRegistrationCallback(TEST_SUB_ID, regCb); 254 verify(mFeatureManager, never()).unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb); 255 } 256 257 @Test testCarrierConfigChanged()258 public void testCarrierConfigChanged() throws Exception { 259 RcsFeatureController controller = createFeatureController(); 260 // Connect the RcsFeatureManager 261 mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID); 262 verify(mFeatureManager).updateCapabilities(TEST_SUB_ID); 263 controller.addFeature(mMockFeature, RcsFeatureController.Feature.class); 264 265 controller.onCarrierConfigChangedForSubscription(); 266 267 verify(mFeatureManager, times(2)).updateCapabilities(TEST_SUB_ID); 268 verify(mMockFeature).onCarrierConfigChanged(); 269 verify(mMockFeature, never()).onAssociatedSubscriptionUpdated(anyInt()); 270 } 271 272 @Test testChangeSubId()273 public void testChangeSubId() throws Exception { 274 RcsFeatureController controller = createFeatureController(); 275 // Connect the RcsFeatureManager 276 mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID); 277 verify(mFeatureManager).updateCapabilities(TEST_SUB_ID); 278 controller.addFeature(mMockFeature, RcsFeatureController.Feature.class); 279 280 controller.updateAssociatedSubscription(2 /*new subId*/); 281 282 verify(mFeatureManager).updateCapabilities(2 /*new subId*/); 283 verify(mMockFeature).onAssociatedSubscriptionUpdated(2 /*new subId*/); 284 } 285 286 @Test testDestroy()287 public void testDestroy() throws Exception { 288 RcsFeatureController controller = createFeatureController(); 289 // Connect the RcsFeatureManager 290 mConnectorListener.getValue().connectionReady(mFeatureManager, TEST_SUB_ID); 291 controller.addFeature(mMockFeature, RcsFeatureController.Feature.class); 292 controller.destroy(); 293 294 verify(mFeatureConnector).disconnect(); 295 verify(mMockFeature).onRcsDisconnected(); 296 verify(mMockFeature).onDestroy(); 297 assertNull(controller.getFeature(RcsFeatureController.Feature.class)); 298 } 299 createFeatureController()300 private RcsFeatureController createFeatureController() { 301 RcsFeatureController controller = new RcsFeatureController(mContext, 0 /*slotId*/, 302 TEST_SUB_ID, mRegistrationFactory); 303 controller.setFeatureConnectorFactory(mFeatureFactory); 304 doReturn(mFeatureConnector).when(mFeatureFactory).create(any(), anyInt(), 305 mConnectorListener.capture(), any(), any()); 306 controller.connect(); 307 assertNotNull(mConnectorListener.getValue()); 308 return controller; 309 } 310 } 311