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 ImsCallSessionImplBase s = new TestImsCallSessionImpl(profile); 182 mCallSession = (TestImsCallSessionImpl) s; 183 onCallCreate(mCallSession); 184 return s != null ? s : null; 185 } 186 187 @Override setTerminalBasedCallWaitingStatus(boolean enabled)188 public void setTerminalBasedCallWaitingStatus(boolean enabled) { 189 mIsTerminalBasedCallWaitingNotified = true; 190 mIsTerminalBasedCallWaitingEnabled = enabled; 191 mTerminalBasedCallWaitingLatch.countDown(); 192 } 193 194 @Override notifySrvccStarted(Consumer<List<SrvccCall>> cb)195 public void notifySrvccStarted(Consumer<List<SrvccCall>> cb) { 196 mSrvccState = TelephonyManager.SRVCC_STATE_HANDOVER_STARTED; 197 mSrvccStartedCallback = cb; 198 } 199 200 @Override notifySrvccCompleted()201 public void notifySrvccCompleted() { 202 mSrvccState = TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED; 203 mSrvccStartedCallback = null; 204 } 205 206 @Override notifySrvccFailed()207 public void notifySrvccFailed() { 208 mSrvccState = TelephonyManager.SRVCC_STATE_HANDOVER_FAILED; 209 mSrvccStartedCallback = null; 210 } 211 212 @Override notifySrvccCanceled()213 public void notifySrvccCanceled() { 214 mSrvccState = TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED; 215 mSrvccStartedCallback = null; 216 } 217 218 @Override queryMediaQualityStatus(int sessionType)219 public MediaQualityStatus queryMediaQualityStatus(int sessionType) { 220 if (!mCallSession.isInCall()) { 221 Log.d(TAG, "queryMediaQualityStatus: no call."); 222 return null; 223 } 224 MediaQualityStatus status = new MediaQualityStatus(mCallSession.getCallId(), 225 MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, 226 AccessNetworkConstants.TRANSPORT_TYPE_WWAN, 227 0 /*packetLossRate*/, 0 /*jitter*/, 0 /*inactivityTime*/); 228 229 Log.d(TAG, "queryMediaQualityStatus: current status " + status); 230 return status; 231 } 232 setCapabilities(MmTelCapabilities capabilities)233 public void setCapabilities(MmTelCapabilities capabilities) { 234 mCapabilities = capabilities; 235 } 236 getCapabilities()237 public MmTelCapabilities getCapabilities() { 238 return mCapabilities; 239 } 240 getOfferedRtpHeaderExtensionTypes()241 public Set<RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes() { 242 return mOfferedRtpHeaderExtensionTypes; 243 } 244 getOfferedRtpHeaderExtensionLatch()245 public CountDownLatch getOfferedRtpHeaderExtensionLatch() { 246 return mOfferedRtpHeaderExtensionLatch; 247 } 248 getSetMediaThreshold()249 public MediaThreshold getSetMediaThreshold() { 250 Log.d(TAG, "getSetMediaThreshold: " + mSetMediaThreshold); 251 return mSetMediaThreshold; 252 } 253 getSetMediaThresholdLatch(int testPacketLossRate, int testJitter, long testInactivityMillis)254 public CountDownLatch getSetMediaThresholdLatch(int testPacketLossRate, int testJitter, 255 long testInactivityMillis) { 256 mTestPacketLossRateValue = testPacketLossRate; 257 mTestJitterValue = testJitter; 258 mTestInactivityTime = testInactivityMillis; 259 return mSetMediaThresholdLatch; 260 } 261 getImsCallsession()262 public TestImsCallSessionImpl getImsCallsession() { 263 return mCallSession; 264 } 265 isCallSessionCreated()266 public boolean isCallSessionCreated() { 267 return (mCallSession != null); 268 } 269 onIncomingCallReceived(Bundle extras)270 public void onIncomingCallReceived(Bundle extras) { 271 Log.d(TAG, "onIncomingCallReceived"); 272 273 ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile( 274 ImsStreamMediaProfile.AUDIO_QUALITY_AMR, 275 ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE, 276 ImsStreamMediaProfile.VIDEO_QUALITY_NONE, 277 ImsStreamMediaProfile.DIRECTION_INVALID, 278 ImsStreamMediaProfile.RTT_MODE_DISABLED); 279 280 ImsCallProfile callProfile = new ImsCallProfile(ImsCallProfile.SERVICE_TYPE_NORMAL, 281 ImsCallProfile.CALL_TYPE_VOICE, new Bundle(), mediaProfile); 282 283 TestImsCallSessionImpl incomingSession = new TestImsCallSessionImpl(callProfile); 284 mCallSession = incomingSession; 285 286 Executor executor = incomingSession.getExecutor(); 287 executor.execute(() -> { 288 notifyIncomingCall(incomingSession, extras); 289 }); 290 } 291 292 onIncomingCallReceivedReturnListener(Bundle extras)293 public ImsCallSessionListener onIncomingCallReceivedReturnListener(Bundle extras) { 294 Log.d(TAG, "onIncomingCallReceivedReturnListener"); 295 296 ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile( 297 ImsStreamMediaProfile.AUDIO_QUALITY_AMR, 298 ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE, 299 ImsStreamMediaProfile.VIDEO_QUALITY_NONE, 300 ImsStreamMediaProfile.DIRECTION_INVALID, 301 ImsStreamMediaProfile.RTT_MODE_DISABLED); 302 303 ImsCallProfile callProfile = new ImsCallProfile(ImsCallProfile.SERVICE_TYPE_NORMAL, 304 ImsCallProfile.CALL_TYPE_VOICE, new Bundle(), mediaProfile); 305 306 TestImsCallSessionImpl incomingSession = new TestImsCallSessionImpl(callProfile); 307 mCallSession = incomingSession; 308 String callId = mCallSession.getCallId(); 309 310 CompletableFuture<ImsCallSessionListener> future = 311 CompletableFuture.supplyAsync(()-> 312 notifyIncomingCall(incomingSession, callId, extras), 313 incomingSession.getExecutor()); 314 try { 315 ImsCallSessionListener isl = future.get(); 316 return future.get(); 317 } catch (ExecutionException | InterruptedException e) { 318 e.printStackTrace(); 319 return null; 320 } 321 } 322 onCallCreate(TestImsCallSessionImpl session)323 private void onCallCreate(TestImsCallSessionImpl session) { 324 if (sConferenceHelper != null) { 325 sConferenceHelper.addSession(session); 326 session.setConferenceHelper(sConferenceHelper); 327 } 328 } 329 getConferenceHelper()330 public ConferenceHelper getConferenceHelper() { 331 return sConferenceHelper; 332 } 333 getTerminalBasedCallWaitingLatch()334 public CountDownLatch getTerminalBasedCallWaitingLatch() { 335 return mTerminalBasedCallWaitingLatch; 336 } 337 resetTerminalBasedCallWaitingLatch()338 public CountDownLatch resetTerminalBasedCallWaitingLatch() { 339 mIsTerminalBasedCallWaitingNotified = false; 340 mTerminalBasedCallWaitingLatch = new CountDownLatch(1); 341 return mTerminalBasedCallWaitingLatch; 342 } 343 isTerminalBasedCallWaitingNotified()344 public boolean isTerminalBasedCallWaitingNotified() { 345 return mIsTerminalBasedCallWaitingNotified; 346 } 347 isTerminalBasedCallWaitingEnabled()348 public boolean isTerminalBasedCallWaitingEnabled() { 349 return mIsTerminalBasedCallWaitingEnabled; 350 } 351 getSrvccStateLatch()352 public CountDownLatch getSrvccStateLatch() { 353 return mSrvccStateLatch; 354 } 355 getSrvccState()356 public int getSrvccState() { 357 return mSrvccState; 358 } 359 notifySrvccCall(List<SrvccCall> profiles)360 public void notifySrvccCall(List<SrvccCall> profiles) { 361 if (mSrvccStartedCallback != null) { 362 mSrvccStartedCallback.accept(profiles); 363 mSrvccStartedCallback = null; 364 } 365 } 366 resetSrvccState()367 public void resetSrvccState() { 368 mSrvccStateLatch = new CountDownLatch(1); 369 mSrvccState = TelephonyManager.SRVCC_STATE_HANDOVER_NONE; 370 mSrvccStartedCallback = null; 371 } 372 } 373