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