1 /*
2  * Copyright (C) 2013 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 com.android.internal.telephony;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.net.Uri;
26 import android.os.AsyncResult;
27 import android.os.BadParcelableException;
28 import android.os.Bundle;
29 import android.os.PersistableBundle;
30 import android.telephony.AccessNetworkConstants;
31 import android.telephony.CellSignalStrengthLte;
32 import android.telephony.NetworkRegistrationInfo;
33 import android.telephony.ServiceState;
34 import android.telephony.SignalStrength;
35 import android.telephony.TelephonyManager;
36 import android.telephony.ims.ImsCallProfile;
37 import android.telephony.ims.ImsConferenceState;
38 import android.telephony.ims.ImsExternalCallState;
39 import android.telephony.ims.ImsReasonInfo;
40 
41 import com.android.ims.ImsCall;
42 import com.android.internal.telephony.gsm.SuppServiceNotification;
43 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
44 import com.android.internal.telephony.imsphone.ImsPhone;
45 import com.android.internal.telephony.imsphone.ImsPhoneCall;
46 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
47 import com.android.internal.telephony.test.TestConferenceEventPackageParser;
48 import com.android.internal.telephony.util.TelephonyUtils;
49 import com.android.telephony.Rlog;
50 
51 import java.io.File;
52 import java.io.FileInputStream;
53 import java.io.FileNotFoundException;
54 import java.lang.reflect.Field;
55 import java.util.ArrayList;
56 import java.util.Arrays;
57 import java.util.List;
58 
59 /**
60  * Telephony tester receives the following intents where {name} is the phone name
61  *
62  * adb shell am broadcast -a com.android.internal.telephony.{name}.action_detached
63  * adb shell am broadcast -a com.android.internal.telephony.{name}.action_attached
64  * adb shell am broadcast -a com.android.internal.telephony.TestConferenceEventPackage -e filename
65  *      test_filename.xml
66  * adb shell am broadcast -a com.android.internal.telephony.TestServiceState --ei data_rat 10 --ei
67  *      data_roaming_type 3
68  * adb shell am broadcast -a com.android.internal.telephony.TestServiceState --es action reset
69  *
70  */
71 public class TelephonyTester {
72     private static final boolean DBG = true;
73 
74     /**
75      * Test-only intent used to send a test conference event package to the IMS framework.
76      */
77     private static final String ACTION_TEST_CONFERENCE_EVENT_PACKAGE =
78             "com.android.internal.telephony.TestConferenceEventPackage";
79 
80     /**
81      * Test-only intent used to send a test dialog event package to the IMS framework.
82      */
83     private static final String ACTION_TEST_DIALOG_EVENT_PACKAGE =
84             "com.android.internal.telephony.TestDialogEventPackage";
85 
86     private static final String EXTRA_FILENAME = "filename";
87     /**
88      * Used to inject the conference event package by bypassing the ImsCall and doing the
89      * injection via ImsPhoneCallTracker.  This is useful in scenarios where the
90      * adb shell cmd phone ims conference-event-package disable
91      * command is used to disable network CEP data and it is desired to still inject CEP data.
92      * Where the network CEP data is not explicitly disabled using the command above, it is not
93      * necessary to bypass the ImsCall.
94      */
95     private static final String EXTRA_BYPASS_IMSCALL = "bypassImsCall";
96     private static final String EXTRA_STARTPACKAGE = "startPackage";
97     private static final String EXTRA_SENDPACKAGE = "sendPackage";
98     private static final String EXTRA_DIALOGID = "dialogId";
99     private static final String EXTRA_NUMBER = "number";
100     private static final String EXTRA_STATE = "state";
101     private static final String EXTRA_CANPULL = "canPull";
102 
103     /**
104      * Test-only intent used to trigger supp service notification failure.
105      */
106     private static final String ACTION_TEST_SUPP_SRVC_FAIL =
107             "com.android.internal.telephony.TestSuppSrvcFail";
108     private static final String EXTRA_FAILURE_CODE = "failureCode";
109 
110     /**
111      * Test-only intent used to trigger the signalling which occurs when a handover to WIFI fails.
112      */
113     private static final String ACTION_TEST_HANDOVER_FAIL =
114             "com.android.internal.telephony.TestHandoverFail";
115 
116     /**
117      * Test-only intent used to trigger signalling of a
118      * {@link com.android.internal.telephony.gsm.SuppServiceNotification} to the {@link ImsPhone}.
119      * Use {@link #EXTRA_CODE} to specify the
120      * {@link com.android.internal.telephony.gsm.SuppServiceNotification#code}.
121      */
122     private static final String ACTION_TEST_SUPP_SRVC_NOTIFICATION =
123             "com.android.internal.telephony.TestSuppSrvcNotification";
124 
125     private static final String EXTRA_CODE = "code";
126     private static final String EXTRA_TYPE = "type";
127 
128     /**
129      * Test-only intent used to trigger signalling that an IMS call is an emergency call.
130      */
131     private static final String ACTION_TEST_IMS_E_CALL =
132             "com.android.internal.telephony.TestImsECall";
133 
134     /**
135      * Test-only intent used to trigger signalling that an IMS call received a DTMF tone.
136      */
137     private static final String ACTION_TEST_RECEIVE_DTMF =
138             "com.android.internal.telephony.TestReceiveDtmf";
139 
140     private static final String EXTRA_DIGIT = "digit";
141 
142     /**
143      * Test-only intent used to trigger a change to the current call's phone number.
144      * Use the {@link #EXTRA_NUMBER} extra to specify the new phone number.
145      */
146     private static final String ACTION_TEST_CHANGE_NUMBER =
147             "com.android.internal.telephony.TestChangeNumber";
148 
149     private static final String ACTION_TEST_SERVICE_STATE =
150             "com.android.internal.telephony.TestServiceState";
151 
152     private static final String EXTRA_ACTION = "action";
153     private static final String EXTRA_PHONE_ID = "phone_id";
154     private static final String EXTRA_VOICE_RAT = "voice_rat";
155     private static final String EXTRA_DATA_RAT = "data_rat";
156     private static final String EXTRA_VOICE_REG_STATE = "voice_reg_state";
157     private static final String EXTRA_DATA_REG_STATE = "data_reg_state";
158     private static final String EXTRA_VOICE_ROAMING_TYPE = "voice_roaming_type";
159     private static final String EXTRA_DATA_ROAMING_TYPE = "data_roaming_type";
160     private static final String EXTRA_NR_FREQUENCY_RANGE = "nr_frequency_range";
161     private static final String EXTRA_NR_STATE = "nr_state";
162     private static final String EXTRA_OPERATOR = "operator";
163     private static final String EXTRA_OPERATOR_RAW = "operator_raw";
164 
165     private static final String ACTION_RESET = "reset";
166 
167     private String mLogTag;
168 
169     private static List<ImsExternalCallState> mImsExternalCallStates = null;
170 
171     private Intent mServiceStateTestIntent;
172     private SignalStrengthTestable mSignalStrengthTest;
173 
174     private Phone mPhone;
175 
176     // The static intent receiver one for all instances and we assume this
177     // is running on the same thread as Dcc.
178     protected BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
179             @Override
180         public void onReceive(Context context, Intent intent) {
181             String action = intent.getAction();
182             try {
183                 if (DBG) log("sIntentReceiver.onReceive: action=" + action);
184                 if (action.equals(mPhone.getActionDetached())) {
185                     log("simulate detaching");
186                     mPhone.getServiceStateTracker().mDetachedRegistrants.get(
187                             AccessNetworkConstants.TRANSPORT_TYPE_WWAN).notifyRegistrants();
188                 } else if (action.equals(mPhone.getActionAttached())) {
189                     log("simulate attaching");
190                     mPhone.getServiceStateTracker().mAttachedRegistrants.get(
191                             AccessNetworkConstants.TRANSPORT_TYPE_WWAN).notifyRegistrants();
192                 } else if (action.equals(ACTION_TEST_CONFERENCE_EVENT_PACKAGE)) {
193                     log("inject simulated conference event package");
194                     handleTestConferenceEventPackage(context,
195                             intent.getStringExtra(EXTRA_FILENAME),
196                             intent.getBooleanExtra(EXTRA_BYPASS_IMSCALL, false));
197                 } else if (action.equals(ACTION_TEST_DIALOG_EVENT_PACKAGE)) {
198                     log("handle test dialog event package intent");
199                     handleTestDialogEventPackageIntent(intent);
200                 } else if (action.equals(ACTION_TEST_SUPP_SRVC_FAIL)) {
201                     log("handle test supp svc failed intent");
202                     handleSuppServiceFailedIntent(intent);
203                 } else if (action.equals(ACTION_TEST_HANDOVER_FAIL)) {
204                     log("handle handover fail test intent");
205                     handleHandoverFailedIntent();
206                 } else if (action.equals(ACTION_TEST_SUPP_SRVC_NOTIFICATION)) {
207                     log("handle supp service notification test intent");
208                     sendTestSuppServiceNotification(intent);
209                 } else if (action.equals(ACTION_TEST_SERVICE_STATE)) {
210                     log("handle test service state changed intent");
211                     setServiceStateTestIntent(intent);
212                 } else if (action.equals(ACTION_TEST_IMS_E_CALL)) {
213                     log("handle test IMS ecall intent");
214                     testImsECall();
215                 } else if (action.equals(ACTION_TEST_RECEIVE_DTMF)) {
216                     log("handle test DTMF intent");
217                     testImsReceiveDtmf(intent);
218                 } else if (action.equals(ACTION_TEST_CHANGE_NUMBER)) {
219                     log("handle test change number intent");
220                     testChangeNumber(intent);
221                 } else {
222                     if (DBG) log("onReceive: unknown action=" + action);
223                 }
224             } catch (BadParcelableException e) {
225                 Rlog.w(mLogTag, e);
226             }
227         }
228     };
229 
TelephonyTester(Phone phone)230     TelephonyTester(Phone phone) {
231         mPhone = phone;
232 
233         if (TelephonyUtils.IS_DEBUGGABLE) {
234             mLogTag = "TelephonyTester-" + mPhone.getPhoneId();
235             IntentFilter filter = new IntentFilter();
236 
237             filter.addAction(mPhone.getActionDetached());
238             log("register for intent action=" + mPhone.getActionDetached());
239 
240             filter.addAction(mPhone.getActionAttached());
241             log("register for intent action=" + mPhone.getActionAttached());
242 
243             if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
244                 log("register for intent action=" + ACTION_TEST_CONFERENCE_EVENT_PACKAGE);
245                 filter.addAction(ACTION_TEST_CONFERENCE_EVENT_PACKAGE);
246                 filter.addAction(ACTION_TEST_DIALOG_EVENT_PACKAGE);
247                 filter.addAction(ACTION_TEST_SUPP_SRVC_FAIL);
248                 filter.addAction(ACTION_TEST_HANDOVER_FAIL);
249                 filter.addAction(ACTION_TEST_SUPP_SRVC_NOTIFICATION);
250                 filter.addAction(ACTION_TEST_IMS_E_CALL);
251                 filter.addAction(ACTION_TEST_RECEIVE_DTMF);
252                 mImsExternalCallStates = new ArrayList<ImsExternalCallState>();
253             }
254 
255             filter.addAction(ACTION_TEST_SERVICE_STATE);
256             log("register for intent action=" + ACTION_TEST_SERVICE_STATE);
257 
258             filter.addAction(ACTION_TEST_CHANGE_NUMBER);
259             log("register for intent action=" + ACTION_TEST_CHANGE_NUMBER);
260             phone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone.getHandler(),
261                     Context.RECEIVER_EXPORTED);
262         }
263     }
264 
dispose()265     void dispose() {
266         if (TelephonyUtils.IS_DEBUGGABLE) {
267             mPhone.getContext().unregisterReceiver(mIntentReceiver);
268         }
269     }
270 
log(String s)271     private void log(String s) {
272         Rlog.d(mLogTag, s);
273     }
274 
handleSuppServiceFailedIntent(Intent intent)275     private void handleSuppServiceFailedIntent(Intent intent) {
276         ImsPhone imsPhone = (ImsPhone) mPhone;
277         if (imsPhone == null) {
278             return;
279         }
280         int code = intent.getIntExtra(EXTRA_FAILURE_CODE, 0);
281         imsPhone.notifySuppServiceFailed(PhoneInternalInterface.SuppService.values()[code]);
282     }
283 
handleHandoverFailedIntent()284     private void handleHandoverFailedIntent() {
285         // Attempt to get the active IMS call
286         ImsCall imsCall = getImsCall();
287         if (imsCall == null) {
288             return;
289         }
290 
291         imsCall.getImsCallSessionListenerProxy().callSessionHandoverFailed(imsCall.getCallSession(),
292                 TelephonyManager.NETWORK_TYPE_LTE, TelephonyManager.NETWORK_TYPE_IWLAN,
293                 new ImsReasonInfo());
294     }
295 
296     /**
297      * Handles request to send a test conference event package to the active Ims call.
298      *
299      * @see com.android.internal.telephony.test.TestConferenceEventPackageParser
300      * @param context The context.
301      * @param fileName The name of the test conference event package file to read.
302      */
handleTestConferenceEventPackage(Context context, String fileName, boolean isBypassingImsCall)303     private void handleTestConferenceEventPackage(Context context, String fileName,
304             boolean isBypassingImsCall) {
305         // Attempt to get the active IMS call before parsing the test XML file.
306         ImsPhone imsPhone = (ImsPhone) mPhone;
307         if (imsPhone == null) {
308             return;
309         }
310 
311         ImsPhoneCallTracker tracker = (ImsPhoneCallTracker) imsPhone.getCallTracker();
312 
313         File packageFile = new File(context.getFilesDir(), fileName);
314         final FileInputStream is;
315         try {
316             is = new FileInputStream(packageFile);
317         } catch (FileNotFoundException ex) {
318             log("Test conference event package file not found: " + packageFile.getAbsolutePath());
319             return;
320         }
321 
322         TestConferenceEventPackageParser parser = new TestConferenceEventPackageParser(is);
323         ImsConferenceState imsConferenceState = parser.parse();
324         if (imsConferenceState == null) {
325             return;
326         }
327 
328         if (isBypassingImsCall) {
329             tracker.injectTestConferenceState(imsConferenceState);
330         } else {
331             ImsPhoneCall imsPhoneCall = imsPhone.getForegroundCall();
332             if (imsPhoneCall == null) {
333                 return;
334             }
335 
336             ImsCall imsCall = imsPhoneCall.getImsCall();
337             if (imsCall == null) {
338                 return;
339             }
340 
341             imsCall.conferenceStateUpdated(imsConferenceState);
342         }
343     }
344 
345     /**
346      * Handles intents containing test dialog event package data.
347      *
348      * @param intent
349      */
handleTestDialogEventPackageIntent(Intent intent)350     private void handleTestDialogEventPackageIntent(Intent intent) {
351         ImsPhone imsPhone = (ImsPhone) mPhone;
352         if (imsPhone == null) {
353             return;
354         }
355         ImsExternalCallTracker externalCallTracker = imsPhone.getExternalCallTracker();
356         if (externalCallTracker == null) {
357             return;
358         }
359 
360         if (intent.hasExtra(EXTRA_STARTPACKAGE)) {
361             mImsExternalCallStates.clear();
362         } else if (intent.hasExtra(EXTRA_SENDPACKAGE)) {
363             externalCallTracker.refreshExternalCallState(mImsExternalCallStates);
364             mImsExternalCallStates.clear();
365         } else if (intent.hasExtra(EXTRA_DIALOGID)) {
366             ImsExternalCallState state = new ImsExternalCallState(
367                     intent.getIntExtra(EXTRA_DIALOGID, 0),
368                     Uri.parse(intent.getStringExtra(EXTRA_NUMBER)),
369                     intent.getBooleanExtra(EXTRA_CANPULL, true),
370                     intent.getIntExtra(EXTRA_STATE,
371                             ImsExternalCallState.CALL_STATE_CONFIRMED),
372                     ImsCallProfile.CALL_TYPE_VOICE,
373                     false /* isHeld */
374                     );
375             mImsExternalCallStates.add(state);
376         }
377     }
378 
sendTestSuppServiceNotification(Intent intent)379     private void sendTestSuppServiceNotification(Intent intent) {
380         if (intent.hasExtra(EXTRA_CODE) && intent.hasExtra(EXTRA_TYPE)) {
381             int code = intent.getIntExtra(EXTRA_CODE, -1);
382             int type = intent.getIntExtra(EXTRA_TYPE, -1);
383             ImsPhone imsPhone = (ImsPhone) mPhone;
384             if (imsPhone == null) {
385                 return;
386             }
387             log("Test supp service notification:" + code);
388             SuppServiceNotification suppServiceNotification = new SuppServiceNotification();
389             suppServiceNotification.code = code;
390             suppServiceNotification.notificationType = type;
391             imsPhone.notifySuppSvcNotification(suppServiceNotification);
392         }
393     }
394 
395     /**
396      * Testable signal strength that mocks its fields.
397      */
398     private class SignalStrengthTestable extends SignalStrength {
SignalStrengthTestable()399         private SignalStrengthTestable() {
400             super();
401         }
402 
mockLevel(int level)403         public void mockLevel(int level) {
404             try {
405                 Field lteField = SignalStrength.class.getDeclaredField("mLte");
406                 lteField.setAccessible(true);
407                 CellSignalStrengthLte lte = (CellSignalStrengthLte) lteField.get(this);
408 
409                 Field lvlField = CellSignalStrengthLte.class.getDeclaredField("mLevel");
410                 lvlField.setAccessible(true);
411                 lvlField.set(lte, level);
412             } catch (Exception e) {
413                 log("SignalStrengthTestable: mockLevel " + e);
414             }
415         }
416 
417         @Override
updateLevel(PersistableBundle cc, ServiceState ss)418         public void updateLevel(PersistableBundle cc, ServiceState ss) {
419             log("SignalStrengthTestable: updateLevel: do nothing ");
420         }
421 
422         @Override
toString()423         public String toString() {
424             return "SignalStrengthTestable-" + getLevel();
425         }
426     }
427 
428     /** {@link android.telephony.SignalStrength} */
setSignalStrength(int level)429     public void setSignalStrength(int level) {
430         if (level > -1) {
431             log("setSignalStrength: level " + level);
432             mSignalStrengthTest = new SignalStrengthTestable();
433             mSignalStrengthTest.mockLevel(level);
434             AsyncResult ar = new AsyncResult(null, mSignalStrengthTest, null);
435             mPhone.getSignalStrengthController().sendMessage(mPhone.getSignalStrengthController()
436                     .obtainMessage(SignalStrengthController.EVENT_POLL_SIGNAL_STRENGTH_DONE, ar));
437         } else {
438             log("setSignalStrength: clear mock");
439             mSignalStrengthTest = null;
440             mPhone.getSignalStrengthController().getSignalStrengthFromCi();
441         }
442     }
443 
444     /** {@link android.telephony.SignalStrength} */
445     @Nullable
getOverriddenSignalStrength()446     public SignalStrength getOverriddenSignalStrength() {
447         return mSignalStrengthTest;
448     }
449 
450     /**
451      * Set the service state test intent.
452      *
453      * @param intent The service state test intent.
454      */
setServiceStateTestIntent(@onNull Intent intent)455     public void setServiceStateTestIntent(@NonNull Intent intent) {
456         // Don't process if the intent is not prepared for this phone slot.
457         if (mPhone.getPhoneId() != intent.getIntExtra(EXTRA_PHONE_ID, mPhone.getPhoneId())) return;
458         mServiceStateTestIntent = intent;
459         // Trigger the service state update. The replacement will be done in
460         // overrideServiceState().
461         mPhone.getServiceStateTracker().sendEmptyMessage(
462                 ServiceStateTracker.EVENT_NETWORK_STATE_CHANGED);
463     }
464 
overrideServiceState(ServiceState ss)465     void overrideServiceState(ServiceState ss) {
466         if (mServiceStateTestIntent == null || ss == null) return;
467         if (mServiceStateTestIntent.hasExtra(EXTRA_ACTION)
468                 && ACTION_RESET.equals(mServiceStateTestIntent.getStringExtra(EXTRA_ACTION))) {
469             log("Service state override reset");
470             return;
471         }
472 
473         if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_REG_STATE)) {
474             int state = mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE,
475                     ServiceState.STATE_OUT_OF_SERVICE);
476             ss.setVoiceRegState(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_REG_STATE,
477                     ServiceState.STATE_OUT_OF_SERVICE));
478             NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
479                     NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
480             NetworkRegistrationInfo.Builder builder = new NetworkRegistrationInfo.Builder(nri);
481             if (state == ServiceState.STATE_IN_SERVICE) {
482                 builder.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
483             } else {
484                 builder.setRegistrationState(
485                         NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
486             }
487             ss.addNetworkRegistrationInfo(builder.build());
488             log("Override voice service state with " + ss.getState());
489         }
490         if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_REG_STATE)) {
491             int state = mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE,
492                     ServiceState.STATE_OUT_OF_SERVICE);
493             ss.setDataRegState(state);
494             NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
495                     NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
496             NetworkRegistrationInfo.Builder builder = new NetworkRegistrationInfo.Builder(nri);
497             if (state == ServiceState.STATE_IN_SERVICE) {
498                 builder.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
499             } else {
500                 builder.setRegistrationState(
501                         NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
502             }
503             ss.addNetworkRegistrationInfo(builder.build());
504             log("Override data service state with " + ss.getDataRegistrationState());
505         }
506         if (mServiceStateTestIntent.hasExtra(EXTRA_OPERATOR)) {
507             String[] data = mServiceStateTestIntent.getStringExtra(EXTRA_OPERATOR).split(",");
508             String operatorAlphaLong = data.length > 0 ? data[0] : "";
509             String operatorAlphaShort = data.length > 1 ? data[1] : operatorAlphaLong;
510             String operatorNumeric = data.length > 2 ? data[2] : "";
511             ss.setOperatorName(operatorAlphaLong, operatorAlphaShort, operatorNumeric);
512             log("Override operator with " + Arrays.toString(data));
513         }
514         if (mServiceStateTestIntent.hasExtra(EXTRA_OPERATOR_RAW)) {
515             String operator_raw = mServiceStateTestIntent.getStringExtra(EXTRA_OPERATOR_RAW);
516             ss.setOperatorAlphaLongRaw(operator_raw);
517             ss.setOperatorAlphaShortRaw(operator_raw);
518             log("Override operator_raw with " + operator_raw);
519         }
520         if (mServiceStateTestIntent.hasExtra(EXTRA_NR_FREQUENCY_RANGE)) {
521             ss.setNrFrequencyRange(mServiceStateTestIntent.getIntExtra(EXTRA_NR_FREQUENCY_RANGE,
522                     ServiceState.FREQUENCY_RANGE_UNKNOWN));
523             log("Override NR frequency range with " + ss.getNrFrequencyRange());
524         }
525         if (mServiceStateTestIntent.hasExtra(EXTRA_NR_STATE)) {
526             NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
527                     NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
528             if (nri == null) {
529                 nri = new NetworkRegistrationInfo.Builder()
530                         .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
531                         .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
532                         .build();
533             }
534             nri.setNrState(mServiceStateTestIntent.getIntExtra(EXTRA_NR_STATE,
535                     NetworkRegistrationInfo.NR_STATE_NONE));
536             ss.addNetworkRegistrationInfo(nri);
537             log("Override NR state with " + ss.getNrState());
538         }
539         if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_RAT)) {
540             NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
541                     NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
542             if (nri == null) {
543                 nri = new NetworkRegistrationInfo.Builder()
544                         .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
545                         .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
546                         .build();
547             }
548             nri.setAccessNetworkTechnology(ServiceState.rilRadioTechnologyToNetworkType(
549                     mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_RAT,
550                     ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)));
551             ss.addNetworkRegistrationInfo(nri);
552             log("Override voice rat with " + ss.getRilVoiceRadioTechnology());
553         }
554         if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_RAT)) {
555             NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
556                     NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
557             if (nri == null) {
558                 nri = new NetworkRegistrationInfo.Builder()
559                         .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
560                         .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
561                         .build();
562             }
563             nri.setAccessNetworkTechnology(ServiceState.rilRadioTechnologyToNetworkType(
564                     mServiceStateTestIntent.getIntExtra(EXTRA_DATA_RAT,
565                     ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)));
566             ss.addNetworkRegistrationInfo(nri);
567             log("Override data rat with " + ss.getRilDataRadioTechnology());
568         }
569         if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_ROAMING_TYPE)) {
570             NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
571                     NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
572             if (nri == null) {
573                 nri = new NetworkRegistrationInfo.Builder()
574                         .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
575                         .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
576                         .build();
577             }
578             nri.setRoamingType(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_ROAMING_TYPE,
579                     ServiceState.ROAMING_TYPE_UNKNOWN));
580             ss.addNetworkRegistrationInfo(nri);
581             log("Override voice roaming type with " + ss.getVoiceRoamingType());
582         }
583         if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_ROAMING_TYPE)) {
584             NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
585                     NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
586             if (nri == null) {
587                 nri = new NetworkRegistrationInfo.Builder()
588                         .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
589                         .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
590                         .build();
591             }
592             nri.setRoamingType(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_ROAMING_TYPE,
593                     ServiceState.ROAMING_TYPE_UNKNOWN));
594             ss.addNetworkRegistrationInfo(nri);
595             log("Override data roaming type with " + ss.getDataRoamingType());
596         }
597     }
598 
testImsECall()599     void testImsECall() {
600         // Attempt to get the active IMS call before parsing the test XML file.
601         ImsCall imsCall = getImsCall();
602         if (imsCall == null) return;
603 
604         ImsCallProfile callProfile = imsCall.getCallProfile();
605         Bundle extras = callProfile.getCallExtras();
606         if (extras == null) {
607             extras = new Bundle();
608         }
609         extras.putBoolean(ImsCallProfile.EXTRA_EMERGENCY_CALL, true);
610         callProfile.mCallExtras = extras;
611         imsCall.getImsCallSessionListenerProxy().callSessionUpdated(imsCall.getSession(),
612                 callProfile);
613     }
614 
getImsCall()615     private ImsCall getImsCall() {
616         ImsPhone imsPhone = (ImsPhone) mPhone;
617         if (imsPhone == null) {
618             return null;
619         }
620 
621         ImsPhoneCall imsPhoneCall = imsPhone.getForegroundCall();
622         if (imsPhoneCall == null) {
623             return null;
624         }
625 
626         ImsCall imsCall = imsPhoneCall.getImsCall();
627         if (imsCall == null) {
628             return null;
629         }
630         return imsCall;
631     }
632 
testImsReceiveDtmf(Intent intent)633     void testImsReceiveDtmf(Intent intent) {
634         if (!intent.hasExtra(EXTRA_DIGIT)) {
635             return;
636         }
637         char digit = intent.getStringExtra(EXTRA_DIGIT).charAt(0);
638 
639         ImsCall imsCall = getImsCall();
640         if (imsCall == null) {
641             return;
642         }
643 
644         imsCall.getImsCallSessionListenerProxy().callSessionDtmfReceived(digit);
645     }
646 
testChangeNumber(Intent intent)647     void testChangeNumber(Intent intent) {
648         if (!intent.hasExtra(EXTRA_NUMBER)) {
649             return;
650         }
651 
652         String newNumber = intent.getStringExtra(EXTRA_NUMBER);
653 
654         // Update all the calls.
655         mPhone.getForegroundCall().getConnections()
656                 .stream()
657                 .forEach(c -> {
658                     c.setAddress(newNumber, PhoneConstants.PRESENTATION_ALLOWED);
659                     c.setDialString(newNumber);
660                 });
661 
662         // <sigh>
663         if (mPhone instanceof GsmCdmaPhone) {
664             ((GsmCdmaPhone) mPhone).notifyPhoneStateChanged();
665             ((GsmCdmaPhone) mPhone).notifyPreciseCallStateChanged();
666         } else if (mPhone instanceof ImsPhone) {
667             ((ImsPhone) mPhone).notifyPhoneStateChanged();
668             ((ImsPhone) mPhone).notifyPreciseCallStateChanged();
669         }
670     }
671 }
672