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