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.app.Service;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.os.Binder;
23 import android.os.Handler;
24 import android.os.HandlerThread;
25 import android.os.IBinder;
26 import android.os.Looper;
27 import android.os.Message;
28 import android.telephony.ims.ImsService;
29 import android.telephony.ims.feature.MmTelFeature;
30 import android.telephony.ims.feature.RcsFeature;
31 import android.telephony.ims.stub.ImsConfigImplBase;
32 import android.telephony.ims.stub.ImsFeatureConfiguration;
33 import android.telephony.ims.stub.ImsRegistrationImplBase;
34 import android.telephony.ims.stub.SipTransportImplBase;
35 import android.util.Log;
36 
37 import androidx.annotation.NonNull;
38 import androidx.annotation.Nullable;
39 
40 import java.util.HashSet;
41 import java.util.concurrent.CountDownLatch;
42 import java.util.concurrent.Executor;
43 import java.util.concurrent.TimeUnit;
44 
45 /**
46  * A Test ImsService that will verify ImsService functionality.
47  */
48 public class TestImsService extends Service {
49 
50     private static final String TAG = "CtsImsTestImsService";
51     private static MessageExecutor sMessageExecutor = null;
52 
53     private TestImsRegistration mImsRegistrationImplBase;
54     private TestRcsFeature mTestRcsFeature;
55     private TestMmTelFeature mTestMmTelFeature;
56     private TestImsConfig mTestImsConfig;
57     private TestSipTransport mTestSipTransport;
58     private ImsService mTestImsService;
59     private ImsService mTestImsServiceCompat;
60     private Executor mExecutor = Runnable::run;
61     private boolean mIsEnabled = false;
62     private boolean mSetNullRcsBinding = false;
63     private boolean mIsSipTransportImplemented = false;
64     private boolean mIsTestTypeExecutor = false;
65     private boolean mIsImsServiceCompat = false;
66     private long mCapabilities = 0;
67     private ImsFeatureConfiguration mFeatureConfig;
68     protected boolean mIsTelephonyBound = false;
69     private HashSet<Integer> mSubIDs = new HashSet<Integer>();
70     protected final Object mLock = new Object();
71 
72     public static final int LATCH_FEATURES_READY = 0;
73     public static final int LATCH_ENABLE_IMS = 1;
74     public static final int LATCH_DISABLE_IMS = 2;
75     public static final int LATCH_CREATE_MMTEL = 3;
76     public static final int LATCH_CREATE_RCS = 4;
77     public static final int LATCH_REMOVE_MMTEL = 5;
78     public static final int LATCH_REMOVE_RCS = 6;
79     public static final int LATCH_MMTEL_READY = 7;
80     public static final int LATCH_RCS_READY = 8;
81     public static final int LATCH_MMTEL_CAP_SET = 9;
82     public static final int LATCH_RCS_CAP_SET = 10;
83     public static final int LATCH_UCE_LISTENER_SET = 11;
84     public static final int LATCH_UCE_REQUEST_PUBLISH = 12;
85     public static final int LATCH_ON_UNBIND = 13;
86     public static final int LATCH_LAST_MESSAGE_EXECUTE = 14;
87     private static final int LATCH_MAX = 15;
88     private static final int WAIT_FOR_EXIT_TEST = 2000;
89     protected static final CountDownLatch[] sLatches = new CountDownLatch[LATCH_MAX];
90     static {
91         for (int i = 0; i < LATCH_MAX; i++) {
92             sLatches[i] = new CountDownLatch(1);
93         }
94     }
95 
96     interface RemovedListener {
onRemoved()97         void onRemoved();
98     }
99     interface ReadyListener {
onReady()100         void onReady();
101     }
102     interface CapabilitiesSetListener {
onSet()103         void onSet();
104     }
105     interface RcsCapabilityExchangeEventListener {
onSet()106         void onSet();
107     }
108     interface DeviceCapPublishListener {
onPublish()109         void onPublish();
110     }
111 
112     // This is defined here instead TestImsService extending ImsService directly because the GTS
113     // tests were failing to run on pre-P devices. Not sure why, but TestImsService is loaded
114     // even if it isn't used.
115     private class ImsServiceUT extends ImsService {
116 
ImsServiceUT(Context context)117         ImsServiceUT(Context context) {
118             // As explained above, ImsServiceUT is created in order to get around classloader
119             // restrictions. Attach the base context from the wrapper ImsService.
120             if (getBaseContext() == null) {
121                 attachBaseContext(context);
122             }
123 
124             if (mIsTestTypeExecutor) {
125                 mImsRegistrationImplBase = new TestImsRegistration(mExecutor);
126                 mTestSipTransport = new TestSipTransport(mExecutor);
127                 mTestImsConfig = new TestImsConfig(mExecutor);
128             } else {
129                 mImsRegistrationImplBase = new TestImsRegistration();
130                 mTestImsConfig = new TestImsConfig();
131                 mTestSipTransport = new TestSipTransport();
132             }
133         }
134 
135         @Override
querySupportedImsFeatures()136         public ImsFeatureConfiguration querySupportedImsFeatures() {
137             return getFeatureConfig();
138         }
139 
140         @Override
getImsServiceCapabilities()141         public long getImsServiceCapabilities() {
142             return mCapabilities;
143         }
144 
145         @Override
readyForFeatureCreation()146         public void readyForFeatureCreation() {
147             synchronized (mLock) {
148                 countDownLatch(LATCH_FEATURES_READY);
149             }
150         }
151 
152         @Override
enableImsForSubscription(int slotId, int subId)153         public void enableImsForSubscription(int slotId, int subId) {
154             synchronized (mLock) {
155                 countDownLatch(LATCH_ENABLE_IMS);
156                 mSubIDs.add(subId);
157                 setIsEnabled(true);
158             }
159         }
160 
161         @Override
disableImsForSubscription(int slotId, int subId)162         public void disableImsForSubscription(int slotId, int subId) {
163             synchronized (mLock) {
164                 countDownLatch(LATCH_DISABLE_IMS);
165                 mSubIDs.add(subId);
166                 setIsEnabled(false);
167             }
168         }
169 
170         @Override
createRcsFeatureForSubscription(int slotId, int subId)171         public RcsFeature createRcsFeatureForSubscription(int slotId, int subId) {
172             TestImsService.ReadyListener readyListener = () -> {
173                 synchronized (mLock) {
174                     countDownLatch(LATCH_RCS_READY);
175                 }
176             };
177 
178             TestImsService.RemovedListener removedListener = () -> {
179                 synchronized (mLock) {
180                     countDownLatch(LATCH_REMOVE_RCS);
181                     mTestRcsFeature = null;
182                 }
183             };
184 
185             TestImsService.CapabilitiesSetListener setListener = () -> {
186                 synchronized (mLock) {
187                     countDownLatch(LATCH_RCS_CAP_SET);
188                 }
189             };
190 
191             TestImsService.RcsCapabilityExchangeEventListener capExchangeEventListener = () -> {
192                 synchronized (mLock) {
193                     countDownLatch(LATCH_UCE_LISTENER_SET);
194                 }
195             };
196 
197             synchronized (mLock) {
198                 countDownLatch(LATCH_CREATE_RCS);
199                 mSubIDs.add(subId);
200 
201                 if (mIsTestTypeExecutor) {
202                     mTestRcsFeature = new TestRcsFeature(readyListener, removedListener,
203                             setListener, capExchangeEventListener, mExecutor);
204                 } else {
205                     mTestRcsFeature = new TestRcsFeature(readyListener, removedListener,
206                             setListener, capExchangeEventListener);
207                 }
208 
209                 // Setup UCE request listener
210                 mTestRcsFeature.setDeviceCapPublishListener(() -> {
211                     synchronized (mLock) {
212                         countDownLatch(LATCH_UCE_REQUEST_PUBLISH);
213                     }
214                 });
215 
216                 if (mSetNullRcsBinding) {
217                     return null;
218                 }
219                 return mTestRcsFeature;
220             }
221         }
222 
223         @Override
getConfigForSubscription(int slotId, int subId)224         public ImsConfigImplBase getConfigForSubscription(int slotId, int subId) {
225             mSubIDs.add(subId);
226             return mTestImsConfig;
227         }
228 
229         @Override
createMmTelFeatureForSubscription(int slotId, int subId)230         public MmTelFeature createMmTelFeatureForSubscription(int slotId, int subId) {
231             TestImsService.ReadyListener readyListener = () -> {
232                 synchronized (mLock) {
233                     countDownLatch(LATCH_MMTEL_READY);
234                 }
235             };
236 
237             TestImsService.RemovedListener removedListener = () -> {
238                 synchronized (mLock) {
239                     countDownLatch(LATCH_REMOVE_MMTEL);
240                     mTestMmTelFeature = null;
241                 }
242             };
243 
244             TestImsService.CapabilitiesSetListener capSetListener = () -> {
245                 synchronized (mLock) {
246                     countDownLatch(LATCH_MMTEL_CAP_SET);
247                 }
248             };
249 
250             synchronized (mLock) {
251                 countDownLatch(LATCH_CREATE_MMTEL);
252                 mSubIDs.add(subId);
253                 if (mIsTestTypeExecutor) {
254                     mTestMmTelFeature = new TestMmTelFeature(readyListener, removedListener,
255                             capSetListener, mExecutor);
256                 } else {
257                     mTestMmTelFeature = new TestMmTelFeature(readyListener, removedListener,
258                             capSetListener);
259                 }
260 
261                 return mTestMmTelFeature;
262             }
263         }
264 
265         @Override
getRegistrationForSubscription(int slotId, int subId)266         public ImsRegistrationImplBase getRegistrationForSubscription(int slotId, int subId) {
267             mSubIDs.add(subId);
268             return mImsRegistrationImplBase;
269         }
270 
271         @Nullable
272         @Override
getSipTransport(int slotId)273         public SipTransportImplBase getSipTransport(int slotId) {
274             if (mIsSipTransportImplemented) {
275                 return mTestSipTransport;
276             } else {
277                 return null;
278             }
279         }
280 
281         @Override
getExecutor()282         public @NonNull Executor getExecutor() {
283             if (mIsTestTypeExecutor) {
284                 return mExecutor;
285             } else {
286                 mExecutor = Runnable::run;
287                 return mExecutor;
288             }
289         }
290     }
291 
292     private class ImsServiceUT_compat extends ImsService {
293 
ImsServiceUT_compat(Context context)294         ImsServiceUT_compat(Context context) {
295             // As explained above, ImsServiceUT is created in order to get around classloader
296             // restrictions. Attach the base context from the wrapper ImsService.
297             if (getBaseContext() == null) {
298                 attachBaseContext(context);
299             }
300 
301             if (mIsTestTypeExecutor) {
302                 mImsRegistrationImplBase = new TestImsRegistration(mExecutor);
303                 mTestSipTransport = new TestSipTransport(mExecutor);
304                 mTestImsConfig = new TestImsConfig(mExecutor);
305             } else {
306                 mImsRegistrationImplBase = new TestImsRegistration();
307                 mTestImsConfig = new TestImsConfig();
308                 mTestSipTransport = new TestSipTransport();
309             }
310         }
311 
312         @Override
querySupportedImsFeatures()313         public ImsFeatureConfiguration querySupportedImsFeatures() {
314             return getFeatureConfig();
315         }
316 
317         @Override
getImsServiceCapabilities()318         public long getImsServiceCapabilities() {
319             return mCapabilities;
320         }
321 
322         @Override
readyForFeatureCreation()323         public void readyForFeatureCreation() {
324             synchronized (mLock) {
325                 countDownLatch(LATCH_FEATURES_READY);
326             }
327         }
328 
329         @Override
enableIms(int slotId)330         public void enableIms(int slotId) {
331             synchronized (mLock) {
332                 countDownLatch(LATCH_ENABLE_IMS);
333                 setIsEnabled(true);
334             }
335         }
336 
337         @Override
disableIms(int slotId)338         public void disableIms(int slotId) {
339             synchronized (mLock) {
340                 countDownLatch(LATCH_DISABLE_IMS);
341                 setIsEnabled(false);
342             }
343         }
344 
345         @Override
createRcsFeature(int slotId)346         public RcsFeature createRcsFeature(int slotId) {
347 
348             TestImsService.ReadyListener readyListener = () -> {
349                 synchronized (mLock) {
350                     countDownLatch(LATCH_RCS_READY);
351                 }
352             };
353 
354             TestImsService.RemovedListener removedListener = () -> {
355                 synchronized (mLock) {
356                     countDownLatch(LATCH_REMOVE_RCS);
357                     mTestRcsFeature = null;
358                 }
359             };
360 
361             TestImsService.CapabilitiesSetListener setListener = () -> {
362                 synchronized (mLock) {
363                     countDownLatch(LATCH_RCS_CAP_SET);
364                 }
365             };
366 
367             TestImsService.RcsCapabilityExchangeEventListener capExchangeEventListener = () -> {
368                 synchronized (mLock) {
369                     countDownLatch(LATCH_UCE_LISTENER_SET);
370                 }
371             };
372 
373             synchronized (mLock) {
374                 countDownLatch(LATCH_CREATE_RCS);
375 
376                 if (mIsTestTypeExecutor) {
377                     mTestRcsFeature = new TestRcsFeature(readyListener, removedListener,
378                             setListener, capExchangeEventListener, mExecutor);
379                 } else {
380                     mTestRcsFeature = new TestRcsFeature(readyListener, removedListener,
381                             setListener, capExchangeEventListener);
382                 }
383 
384                 // Setup UCE request listener
385                 mTestRcsFeature.setDeviceCapPublishListener(() -> {
386                     synchronized (mLock) {
387                         countDownLatch(LATCH_UCE_REQUEST_PUBLISH);
388                     }
389                 });
390 
391                 if (mSetNullRcsBinding) {
392                     return null;
393                 }
394                 return mTestRcsFeature;
395             }
396         }
397 
398         @Override
getConfig(int slotId)399         public ImsConfigImplBase getConfig(int slotId) {
400             return mTestImsConfig;
401         }
402 
403         @Override
createMmTelFeature(int slotId)404         public MmTelFeature createMmTelFeature(int slotId) {
405             TestImsService.ReadyListener readyListener = () -> {
406                 synchronized (mLock) {
407                     countDownLatch(LATCH_MMTEL_READY);
408                 }
409             };
410 
411             TestImsService.RemovedListener removedListener = () -> {
412                 synchronized (mLock) {
413                     countDownLatch(LATCH_REMOVE_MMTEL);
414                     mTestMmTelFeature = null;
415                 }
416             };
417 
418             TestImsService.CapabilitiesSetListener capSetListener = () -> {
419                 synchronized (mLock) {
420                     countDownLatch(LATCH_MMTEL_CAP_SET);
421                 }
422             };
423 
424             synchronized (mLock) {
425                 countDownLatch(LATCH_CREATE_MMTEL);
426                 if (mIsTestTypeExecutor) {
427                     mTestMmTelFeature = new TestMmTelFeature(readyListener, removedListener,
428                             capSetListener, mExecutor);
429                 } else {
430                     mTestMmTelFeature = new TestMmTelFeature(readyListener, removedListener,
431                             capSetListener);
432                 }
433 
434                 return mTestMmTelFeature;
435             }
436         }
437 
438         @Override
getRegistration(int slotId)439         public ImsRegistrationImplBase getRegistration(int slotId) {
440             return mImsRegistrationImplBase;
441         }
442 
443         @Nullable
444         @Override
getSipTransport(int slotId)445         public SipTransportImplBase getSipTransport(int slotId) {
446             if (mIsSipTransportImplemented) {
447                 return mTestSipTransport;
448             } else {
449                 return null;
450             }
451         }
452 
453         @Override
getExecutor()454         public @NonNull Executor getExecutor() {
455             if (mIsTestTypeExecutor) {
456                 return mExecutor;
457             } else {
458                 mExecutor = Runnable::run;
459                 return mExecutor;
460             }
461         }
462     }
463 
createLooper(String name)464     private static Looper createLooper(String name) {
465         HandlerThread thread = new HandlerThread(name);
466         thread.start();
467 
468         Looper looper = thread.getLooper();
469 
470         if (looper == null) {
471             return Looper.getMainLooper();
472         }
473         return looper;
474     }
475 
476     /**
477      * Executes the tasks in the other thread rather than the calling thread.
478      */
479     public class MessageExecutor extends Handler implements Executor {
MessageExecutor(String name)480         public MessageExecutor(String name) {
481             super(createLooper(name));
482         }
483 
484         @Override
execute(Runnable r)485         public void execute(Runnable r) {
486             Message m = Message.obtain(this, 0, r);
487             m.sendToTarget();
488         }
489 
490         @Override
handleMessage(Message msg)491         public void handleMessage(Message msg) {
492             if (msg.obj instanceof Runnable) {
493                 executeInternal((Runnable) msg.obj);
494             } else {
495                 Log.d(TAG, "[MessageExecutor] handleMessage :: "
496                         + "Not runnable object; ignore the msg=" + msg);
497             }
498         }
499 
executeInternal(Runnable r)500         private void executeInternal(Runnable r) {
501             try {
502                 r.run();
503             } catch (Throwable t) {
504                 Log.d(TAG, "[MessageExecutor] executeInternal :: run task=" + r);
505                 t.printStackTrace();
506             }
507         }
508     }
509 
510     private final LocalBinder mBinder = new LocalBinder();
511     // For local access of this Service.
512     class LocalBinder extends Binder {
getService()513         TestImsService getService() {
514             return TestImsService.this;
515         }
516     }
517 
518     /**
519      * Returns IMS service for CTS test purpose.
520      * @return test Ims service.
521      */
getImsService()522     public ImsService getImsService() {
523         synchronized (mLock) {
524             if (mTestImsService != null) {
525                 return mTestImsService;
526             }
527             mTestImsService = new ImsServiceUT(this);
528             return mTestImsService;
529         }
530     }
531 
getImsServiceCompat()532     protected ImsService getImsServiceCompat() {
533         synchronized (mLock) {
534             if (mTestImsServiceCompat != null) {
535                 return mTestImsServiceCompat;
536             }
537             mTestImsServiceCompat = new ImsServiceUT_compat(this);
538             return mTestImsServiceCompat;
539         }
540     }
541 
542     @Override
onBind(Intent intent)543     public IBinder onBind(Intent intent) {
544         synchronized (mLock) {
545             if ("android.telephony.ims.ImsService".equals(intent.getAction())) {
546                 mIsTelephonyBound = true;
547                 if (mIsImsServiceCompat) {
548                     if (ImsUtils.VDBG) {
549                         Log.d(TAG, "onBind-Remote-Compat");
550                     }
551                     return getImsServiceCompat().onBind(intent);
552                 } else {
553                     if (ImsUtils.VDBG) {
554                         Log.d(TAG, "onBind-Remote");
555                     }
556                     return getImsService().onBind(intent);
557                 }
558             }
559             if (ImsUtils.VDBG) {
560                 Log.i(TAG, "onBind-Local");
561             }
562             return mBinder;
563         }
564     }
565 
566     @Override
onUnbind(Intent intent)567     public boolean onUnbind(Intent intent) {
568         synchronized (mLock) {
569             if ("android.telephony.ims.ImsService".equals(intent.getAction())) {
570                 if (ImsUtils.VDBG)  Log.i(TAG, "onUnbind-Remote");
571                 mIsTelephonyBound = false;
572                 countDownLatch(LATCH_ON_UNBIND);
573             } else {
574                 if (ImsUtils.VDBG)  Log.i(TAG, "onUnbind-Local");
575             }
576             // return false so that onBind is called next time.
577             return false;
578         }
579     }
580 
resetState()581     public void resetState() {
582         synchronized (mLock) {
583             mTestMmTelFeature = null;
584             mTestRcsFeature = null;
585             mIsEnabled = false;
586             mSetNullRcsBinding = false;
587             mIsSipTransportImplemented = false;
588             mIsTestTypeExecutor = false;
589             mIsImsServiceCompat = false;
590             mCapabilities = 0;
591             for (int i = 0; i < LATCH_MAX; i++) {
592                 sLatches[i] = new CountDownLatch(1);
593             }
594 
595             if (sMessageExecutor != null) {
596                 sMessageExecutor.getLooper().quitSafely();
597                 sMessageExecutor = null;
598             }
599             mSubIDs.clear();
600         }
601     }
602 
isTelephonyBound()603     public boolean isTelephonyBound() {
604         return mIsTelephonyBound;
605     }
606 
setExecutorTestType(boolean type)607     public void setExecutorTestType(boolean type) {
608         mIsTestTypeExecutor = type;
609         if (mIsTestTypeExecutor) {
610             if (sMessageExecutor == null) {
611                 sMessageExecutor = new MessageExecutor("TestImsService");
612             }
613             mExecutor = sMessageExecutor;
614         }
615     }
616 
waitForExecutorFinish()617     public void waitForExecutorFinish() {
618         if (mIsTestTypeExecutor && sMessageExecutor != null) {
619             sMessageExecutor.postDelayed(() -> countDownLatch(LATCH_LAST_MESSAGE_EXECUTE), null ,
620                     WAIT_FOR_EXIT_TEST);
621             waitForLatchCountdown(LATCH_LAST_MESSAGE_EXECUTE);
622         }
623     }
624 
setImsServiceCompat()625     public void setImsServiceCompat() {
626         synchronized (mLock) {
627             mIsImsServiceCompat = true;
628         }
629     }
630 
631     // Sets the feature configuration. Make sure to call this before initiating Bind to this
632     // ImsService.
setFeatureConfig(ImsFeatureConfiguration f)633     public void setFeatureConfig(ImsFeatureConfiguration f) {
634         synchronized (mLock) {
635             mFeatureConfig = f;
636         }
637     }
638 
getFeatureConfig()639     public ImsFeatureConfiguration getFeatureConfig() {
640         synchronized (mLock) {
641             return mFeatureConfig;
642         }
643     }
644 
isEnabled()645     public boolean isEnabled() {
646         synchronized (mLock) {
647             return mIsEnabled;
648         }
649     }
650 
setNullRcsBinding()651     public void setNullRcsBinding() {
652         synchronized (mLock) {
653             mSetNullRcsBinding = true;
654         }
655     }
656 
setIsEnabled(boolean isEnabled)657     public void setIsEnabled(boolean isEnabled) {
658         synchronized (mLock) {
659             mIsEnabled = isEnabled;
660         }
661     }
662 
addCapabilities(long capabilities)663     public void addCapabilities(long capabilities) {
664         synchronized (mLock) {
665             mCapabilities |= capabilities;
666         }
667     }
668 
setSipTransportImplemented()669     public void setSipTransportImplemented() {
670         synchronized (mLock) {
671             mIsSipTransportImplemented = true;
672         }
673     }
674 
waitForLatchCountdown(int latchIndex)675     public boolean waitForLatchCountdown(int latchIndex) {
676         return waitForLatchCountdown(latchIndex, ImsUtils.TEST_TIMEOUT_MS);
677     }
678 
waitForLatchCountdown(int latchIndex, long waitMs)679     public boolean waitForLatchCountdown(int latchIndex, long waitMs) {
680         boolean complete = false;
681         try {
682             CountDownLatch latch;
683             synchronized (mLock) {
684                 latch = sLatches[latchIndex];
685             }
686             long startTime = System.currentTimeMillis();
687             complete = latch.await(waitMs, TimeUnit.MILLISECONDS);
688             if (ImsUtils.VDBG) {
689                 Log.i(TAG, "Latch " + latchIndex + " took "
690                         + (System.currentTimeMillis() - startTime) + " ms to count down.");
691             }
692         } catch (InterruptedException e) {
693             // complete == false
694         }
695         synchronized (mLock) {
696             sLatches[latchIndex] = new CountDownLatch(1);
697         }
698         return complete;
699     }
700 
countDownLatch(int latchIndex)701     public void countDownLatch(int latchIndex) {
702         synchronized (mLock) {
703             sLatches[latchIndex].countDown();
704         }
705     }
706 
getMmTelFeature()707     public TestMmTelFeature getMmTelFeature() {
708         synchronized (mLock) {
709             return mTestMmTelFeature;
710         }
711     }
712 
getRcsFeature()713     public TestRcsFeature getRcsFeature() {
714         synchronized (mLock) {
715             return mTestRcsFeature;
716         }
717     }
718 
getSipTransport()719     public TestSipTransport getSipTransport() {
720         synchronized (mLock) {
721             return mTestSipTransport;
722         }
723     }
724 
getImsRegistration()725     public TestImsRegistration getImsRegistration() {
726         synchronized (mLock) {
727             return mImsRegistrationImplBase;
728         }
729     }
730 
getConfig()731     public ImsConfigImplBase getConfig() {
732         return mTestImsConfig;
733     }
734 
getSubIDs()735     public HashSet<Integer> getSubIDs() {
736         return mSubIDs;
737     }
738 }
739