1 /* 2 * Copyright (C) 2019 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 android.telephony.ims.cts; 18 19 import android.os.Bundle; 20 import android.telephony.AccessNetworkConstants; 21 import android.telephony.TelephonyManager; 22 import android.telephony.ims.ImsCallProfile; 23 import android.telephony.ims.ImsCallSessionListener; 24 import android.telephony.ims.ImsStreamMediaProfile; 25 import android.telephony.ims.MediaQualityStatus; 26 import android.telephony.ims.MediaThreshold; 27 import android.telephony.ims.RtpHeaderExtensionType; 28 import android.telephony.ims.SrvccCall; 29 import android.telephony.ims.feature.CapabilityChangeRequest; 30 import android.telephony.ims.feature.MmTelFeature; 31 import android.telephony.ims.stub.ImsCallSessionImplBase; 32 import android.telephony.ims.stub.ImsRegistrationImplBase; 33 import android.util.Log; 34 35 import java.util.List; 36 import java.util.Set; 37 import java.util.concurrent.CompletableFuture; 38 import java.util.concurrent.CountDownLatch; 39 import java.util.concurrent.ExecutionException; 40 import java.util.concurrent.Executor; 41 import java.util.function.Consumer; 42 43 public class TestMmTelFeature extends MmTelFeature { 44 45 private final TestImsService.RemovedListener mRemovedListener; 46 private final TestImsService.ReadyListener mReadyListener; 47 private final TestImsService.CapabilitiesSetListener mCapSetListener; 48 49 private static final String TAG = "CtsTestImsService"; 50 public static ConferenceHelper sConferenceHelper = new ConferenceHelper(); 51 52 private MmTelCapabilities mCapabilities = 53 new MmTelCapabilities(MmTelCapabilities.CAPABILITY_TYPE_SMS); 54 private TestImsSmsImpl mSmsImpl; 55 private Set<RtpHeaderExtensionType> mOfferedRtpHeaderExtensionTypes; 56 private CountDownLatch mOfferedRtpHeaderExtensionLatch = new CountDownLatch(1); 57 private MediaThreshold mSetMediaThreshold; 58 private CountDownLatch mSetMediaThresholdLatch = new CountDownLatch(1); 59 private int mTestPacketLossRateValue; 60 private int mTestJitterValue; 61 private long mTestInactivityTime; 62 private TestImsCallSessionImpl mCallSession; 63 private CountDownLatch mTerminalBasedCallWaitingLatch = new CountDownLatch(1); 64 private boolean mIsTerminalBasedCallWaitingNotified = false; 65 private boolean mIsTerminalBasedCallWaitingEnabled = false; 66 private CountDownLatch mSrvccStateLatch = new CountDownLatch(1); 67 private int mSrvccState = TelephonyManager.SRVCC_STATE_HANDOVER_NONE; 68 private Consumer<List<SrvccCall>> mSrvccStartedCallback; 69 TestMmTelFeature(TestImsService.ReadyListener readyListener, TestImsService.RemovedListener removedListener, TestImsService.CapabilitiesSetListener setListener)70 TestMmTelFeature(TestImsService.ReadyListener readyListener, 71 TestImsService.RemovedListener removedListener, 72 TestImsService.CapabilitiesSetListener setListener) { 73 Log.d(TAG, "TestMmTelFeature with default constructor"); 74 mReadyListener = readyListener; 75 mRemovedListener = removedListener; 76 mCapSetListener = setListener; 77 mSmsImpl = new TestImsSmsImpl(); 78 // Must set the state to READY in the constructor - onFeatureReady depends on the state 79 // being ready. 80 setFeatureState(STATE_READY); 81 } 82 TestMmTelFeature(TestImsService.ReadyListener readyListener, TestImsService.RemovedListener removedListener, TestImsService.CapabilitiesSetListener setListener, Executor executor)83 TestMmTelFeature(TestImsService.ReadyListener readyListener, 84 TestImsService.RemovedListener removedListener, 85 TestImsService.CapabilitiesSetListener setListener, Executor executor) { 86 super(executor); 87 Log.d(TAG, "TestMmTelFeature with Executor constructor"); 88 mReadyListener = readyListener; 89 mRemovedListener = removedListener; 90 mCapSetListener = setListener; 91 mSmsImpl = new TestImsSmsImpl(); 92 // Must set the state to READY in the constructor - onFeatureReady depends on the state 93 // being ready. 94 setFeatureState(STATE_READY); 95 } 96 getSmsImplementation()97 public TestImsSmsImpl getSmsImplementation() { 98 return mSmsImpl; 99 } 100 101 @Override queryCapabilityConfiguration(int capability, int radioTech)102 public boolean queryCapabilityConfiguration(int capability, int radioTech) { 103 if (ImsUtils.VDBG) { 104 Log.d(TAG, "queryCapabilityConfiguration called with capability: " + capability); 105 } 106 return mCapabilities.isCapable(capability); 107 } 108 109 @Override changeEnabledCapabilities(CapabilityChangeRequest request, CapabilityCallbackProxy c)110 public void changeEnabledCapabilities(CapabilityChangeRequest request, 111 CapabilityCallbackProxy c) { 112 List<CapabilityChangeRequest.CapabilityPair> pairs = request.getCapabilitiesToEnable(); 113 for (CapabilityChangeRequest.CapabilityPair pair : pairs) { 114 if (pair.getRadioTech() == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) { 115 mCapabilities.addCapabilities(pair.getCapability()); 116 } 117 } 118 pairs = request.getCapabilitiesToDisable(); 119 for (CapabilityChangeRequest.CapabilityPair pair : pairs) { 120 if (pair.getRadioTech() == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) { 121 mCapabilities.removeCapabilities(pair.getCapability()); 122 } 123 } 124 mCapSetListener.onSet(); 125 } 126 127 @Override onFeatureReady()128 public void onFeatureReady() { 129 if (ImsUtils.VDBG) { 130 Log.d(TAG, "TestMmTelFeature.onFeatureReady called"); 131 } 132 mReadyListener.onReady(); 133 } 134 135 @Override onFeatureRemoved()136 public void onFeatureRemoved() { 137 if (ImsUtils.VDBG) { 138 Log.d(TAG, "TestMmTelFeature.onFeatureRemoved called"); 139 } 140 mRemovedListener.onRemoved(); 141 } 142 143 @Override changeOfferedRtpHeaderExtensionTypes(Set<RtpHeaderExtensionType> extensionTypes)144 public void changeOfferedRtpHeaderExtensionTypes(Set<RtpHeaderExtensionType> extensionTypes) { 145 mOfferedRtpHeaderExtensionTypes = extensionTypes; 146 mOfferedRtpHeaderExtensionLatch.countDown(); 147 } 148 149 @Override setMediaThreshold(int sessionType, MediaThreshold threshold)150 public void setMediaThreshold(int sessionType, MediaThreshold threshold) { 151 Log.d(TAG, "setMediaThreshold" + threshold); 152 int[] packetLossThreshold = threshold.getThresholdsRtpPacketLossRate(); 153 int[] jitterThreshold = threshold.getThresholdsRtpJitterMillis(); 154 long[] inactivityTimeThreshold = threshold.getThresholdsRtpInactivityTimeMillis(); 155 if (packetLossThreshold != null && packetLossThreshold.length == 1 156 && packetLossThreshold[0] == mTestPacketLossRateValue 157 && jitterThreshold[0] == mTestJitterValue 158 && inactivityTimeThreshold[0] == mTestInactivityTime) { 159 mSetMediaThreshold = threshold; 160 mSetMediaThresholdLatch.countDown(); 161 } else { 162 Log.d(TAG, "setMediaThreshold, this is not config update for test" + threshold); 163 } 164 } 165 166 @Override createCallProfile(int serviceType, int callType)167 public ImsCallProfile createCallProfile(int serviceType, int callType) { 168 ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile( 169 ImsStreamMediaProfile.AUDIO_QUALITY_AMR, 170 ImsStreamMediaProfile.DIRECTION_INVALID, 171 ImsStreamMediaProfile.VIDEO_QUALITY_NONE, 172 ImsStreamMediaProfile.DIRECTION_INVALID, 173 ImsStreamMediaProfile.RTT_MODE_DISABLED); 174 ImsCallProfile profile = new ImsCallProfile(serviceType, callType, 175 new Bundle(), mediaProfile); 176 return profile; 177 } 178 179 @Override createCallSession(ImsCallProfile profile)180 public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) { 181 Log.d(TAG, "createCallSession"); 182 ImsCallSessionImplBase s = new TestImsCallSessionImpl(profile); 183 mCallSession = (TestImsCallSessionImpl) s; 184 onCallCreate(mCallSession); 185 return s != null ? s : null; 186 } 187 188 @Override setTerminalBasedCallWaitingStatus(boolean enabled)189 public void setTerminalBasedCallWaitingStatus(boolean enabled) { 190 mIsTerminalBasedCallWaitingNotified = true; 191 mIsTerminalBasedCallWaitingEnabled = enabled; 192 mTerminalBasedCallWaitingLatch.countDown(); 193 } 194 195 @Override notifySrvccStarted(Consumer<List<SrvccCall>> cb)196 public void notifySrvccStarted(Consumer<List<SrvccCall>> cb) { 197 mSrvccState = TelephonyManager.SRVCC_STATE_HANDOVER_STARTED; 198 mSrvccStartedCallback = cb; 199 } 200 201 @Override notifySrvccCompleted()202 public void notifySrvccCompleted() { 203 mSrvccState = TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED; 204 mSrvccStartedCallback = null; 205 } 206 207 @Override notifySrvccFailed()208 public void notifySrvccFailed() { 209 mSrvccState = TelephonyManager.SRVCC_STATE_HANDOVER_FAILED; 210 mSrvccStartedCallback = null; 211 } 212 213 @Override notifySrvccCanceled()214 public void notifySrvccCanceled() { 215 mSrvccState = TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED; 216 mSrvccStartedCallback = null; 217 } 218 219 @Override queryMediaQualityStatus(int sessionType)220 public MediaQualityStatus queryMediaQualityStatus(int sessionType) { 221 if (!mCallSession.isInCall()) { 222 Log.d(TAG, "queryMediaQualityStatus: no call."); 223 return null; 224 } 225 MediaQualityStatus status = new MediaQualityStatus(mCallSession.getCallId(), 226 MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, 227 AccessNetworkConstants.TRANSPORT_TYPE_WWAN, 228 0 /*packetLossRate*/, 0 /*jitter*/, 0 /*inactivityTime*/); 229 230 Log.d(TAG, "queryMediaQualityStatus: current status " + status); 231 return status; 232 } 233 setCapabilities(MmTelCapabilities capabilities)234 public void setCapabilities(MmTelCapabilities capabilities) { 235 mCapabilities = capabilities; 236 } 237 getCapabilities()238 public MmTelCapabilities getCapabilities() { 239 return mCapabilities; 240 } 241 getOfferedRtpHeaderExtensionTypes()242 public Set<RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes() { 243 return mOfferedRtpHeaderExtensionTypes; 244 } 245 getOfferedRtpHeaderExtensionLatch()246 public CountDownLatch getOfferedRtpHeaderExtensionLatch() { 247 return mOfferedRtpHeaderExtensionLatch; 248 } 249 getSetMediaThreshold()250 public MediaThreshold getSetMediaThreshold() { 251 Log.d(TAG, "getSetMediaThreshold: " + mSetMediaThreshold); 252 return mSetMediaThreshold; 253 } 254 getSetMediaThresholdLatch(int testPacketLossRate, int testJitter, long testInactivityMillis)255 public CountDownLatch getSetMediaThresholdLatch(int testPacketLossRate, int testJitter, 256 long testInactivityMillis) { 257 mTestPacketLossRateValue = testPacketLossRate; 258 mTestJitterValue = testJitter; 259 mTestInactivityTime = testInactivityMillis; 260 return mSetMediaThresholdLatch; 261 } 262 getImsCallsession()263 public TestImsCallSessionImpl getImsCallsession() { 264 Log.d(TAG, "getImsCallsession"); 265 return mCallSession; 266 } 267 isCallSessionCreated()268 public boolean isCallSessionCreated() { 269 return (mCallSession != null); 270 } 271 onIncomingCallReceived(Bundle extras)272 public void onIncomingCallReceived(Bundle extras) { 273 Log.d(TAG, "onIncomingCallReceived"); 274 275 ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile( 276 ImsStreamMediaProfile.AUDIO_QUALITY_AMR, 277 ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE, 278 ImsStreamMediaProfile.VIDEO_QUALITY_NONE, 279 ImsStreamMediaProfile.DIRECTION_INVALID, 280 ImsStreamMediaProfile.RTT_MODE_DISABLED); 281 282 ImsCallProfile callProfile = new ImsCallProfile(ImsCallProfile.SERVICE_TYPE_NORMAL, 283 ImsCallProfile.CALL_TYPE_VOICE, new Bundle(), mediaProfile); 284 285 TestImsCallSessionImpl incomingSession = new TestImsCallSessionImpl(callProfile); 286 mCallSession = incomingSession; 287 288 Executor executor = incomingSession.getExecutor(); 289 executor.execute(() -> { 290 notifyIncomingCall(incomingSession, extras); 291 }); 292 } 293 294 onIncomingCallReceivedReturnListener(Bundle extras)295 public ImsCallSessionListener onIncomingCallReceivedReturnListener(Bundle extras) { 296 Log.d(TAG, "onIncomingCallReceivedReturnListener"); 297 298 ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile( 299 ImsStreamMediaProfile.AUDIO_QUALITY_AMR, 300 ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE, 301 ImsStreamMediaProfile.VIDEO_QUALITY_NONE, 302 ImsStreamMediaProfile.DIRECTION_INVALID, 303 ImsStreamMediaProfile.RTT_MODE_DISABLED); 304 305 ImsCallProfile callProfile = new ImsCallProfile(ImsCallProfile.SERVICE_TYPE_NORMAL, 306 ImsCallProfile.CALL_TYPE_VOICE, new Bundle(), mediaProfile); 307 308 TestImsCallSessionImpl incomingSession = new TestImsCallSessionImpl(callProfile); 309 mCallSession = incomingSession; 310 String callId = mCallSession.getCallId(); 311 312 CompletableFuture<ImsCallSessionListener> future = 313 CompletableFuture.supplyAsync(()-> 314 notifyIncomingCall(incomingSession, callId, extras), 315 incomingSession.getExecutor()); 316 try { 317 ImsCallSessionListener isl = future.get(); 318 return future.get(); 319 } catch (ExecutionException | InterruptedException e) { 320 e.printStackTrace(); 321 return null; 322 } 323 } 324 onCallCreate(TestImsCallSessionImpl session)325 private void onCallCreate(TestImsCallSessionImpl session) { 326 if (sConferenceHelper != null) { 327 sConferenceHelper.addSession(session); 328 session.setConferenceHelper(sConferenceHelper); 329 } 330 } 331 getConferenceHelper()332 public ConferenceHelper getConferenceHelper() { 333 return sConferenceHelper; 334 } 335 getTerminalBasedCallWaitingLatch()336 public CountDownLatch getTerminalBasedCallWaitingLatch() { 337 return mTerminalBasedCallWaitingLatch; 338 } 339 resetTerminalBasedCallWaitingLatch()340 public CountDownLatch resetTerminalBasedCallWaitingLatch() { 341 mIsTerminalBasedCallWaitingNotified = false; 342 mTerminalBasedCallWaitingLatch = new CountDownLatch(1); 343 return mTerminalBasedCallWaitingLatch; 344 } 345 isTerminalBasedCallWaitingNotified()346 public boolean isTerminalBasedCallWaitingNotified() { 347 return mIsTerminalBasedCallWaitingNotified; 348 } 349 isTerminalBasedCallWaitingEnabled()350 public boolean isTerminalBasedCallWaitingEnabled() { 351 return mIsTerminalBasedCallWaitingEnabled; 352 } 353 getSrvccStateLatch()354 public CountDownLatch getSrvccStateLatch() { 355 return mSrvccStateLatch; 356 } 357 getSrvccState()358 public int getSrvccState() { 359 return mSrvccState; 360 } 361 notifySrvccCall(List<SrvccCall> profiles)362 public void notifySrvccCall(List<SrvccCall> profiles) { 363 if (mSrvccStartedCallback != null) { 364 mSrvccStartedCallback.accept(profiles); 365 mSrvccStartedCallback = null; 366 } 367 } 368 resetSrvccState()369 public void resetSrvccState() { 370 mSrvccStateLatch = new CountDownLatch(1); 371 mSrvccState = TelephonyManager.SRVCC_STATE_HANDOVER_NONE; 372 mSrvccStartedCallback = null; 373 } 374 } 375