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.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.net.Uri; 24 import android.os.BadParcelableException; 25 import android.os.Build; 26 import android.os.Bundle; 27 import android.telephony.AccessNetworkConstants; 28 import android.telephony.Rlog; 29 import android.telephony.ServiceState; 30 import android.telephony.ims.ImsCallProfile; 31 import android.telephony.ims.ImsConferenceState; 32 import android.telephony.ims.ImsExternalCallState; 33 import android.telephony.ims.ImsReasonInfo; 34 35 import com.android.ims.ImsCall; 36 import com.android.internal.telephony.gsm.SuppServiceNotification; 37 import com.android.internal.telephony.imsphone.ImsExternalCallTracker; 38 import com.android.internal.telephony.imsphone.ImsPhone; 39 import com.android.internal.telephony.imsphone.ImsPhoneCall; 40 import com.android.internal.telephony.test.TestConferenceEventPackageParser; 41 42 import java.io.File; 43 import java.io.FileInputStream; 44 import java.io.FileNotFoundException; 45 import java.util.ArrayList; 46 import java.util.List; 47 48 /** 49 * Telephony tester receives the following intents where {name} is the phone name 50 * 51 * adb shell am broadcast -a com.android.internal.telephony.{name}.action_detached 52 * adb shell am broadcast -a com.android.internal.telephony.{name}.action_attached 53 * adb shell am broadcast -a com.android.internal.telephony.TestConferenceEventPackage -e filename 54 * test_filename.xml 55 * adb shell am broadcast -a com.android.internal.telephony.TestServiceState --ei data_rat 10 --ei 56 * data_roaming_type 3 57 * adb shell am broadcast -a com.android.internal.telephony.TestServiceState --es action reset 58 * 59 */ 60 public class TelephonyTester { 61 private static final String LOG_TAG = "TelephonyTester"; 62 private static final boolean DBG = true; 63 64 /** 65 * Test-only intent used to send a test conference event package to the IMS framework. 66 */ 67 private static final String ACTION_TEST_CONFERENCE_EVENT_PACKAGE = 68 "com.android.internal.telephony.TestConferenceEventPackage"; 69 70 /** 71 * Test-only intent used to send a test dialog event package to the IMS framework. 72 */ 73 private static final String ACTION_TEST_DIALOG_EVENT_PACKAGE = 74 "com.android.internal.telephony.TestDialogEventPackage"; 75 76 private static final String EXTRA_FILENAME = "filename"; 77 private static final String EXTRA_STARTPACKAGE = "startPackage"; 78 private static final String EXTRA_SENDPACKAGE = "sendPackage"; 79 private static final String EXTRA_DIALOGID = "dialogId"; 80 private static final String EXTRA_NUMBER = "number"; 81 private static final String EXTRA_STATE = "state"; 82 private static final String EXTRA_CANPULL = "canPull"; 83 84 /** 85 * Test-only intent used to trigger supp service notification failure. 86 */ 87 private static final String ACTION_TEST_SUPP_SRVC_FAIL = 88 "com.android.internal.telephony.TestSuppSrvcFail"; 89 private static final String EXTRA_FAILURE_CODE = "failureCode"; 90 91 /** 92 * Test-only intent used to trigger the signalling which occurs when a handover to WIFI fails. 93 */ 94 private static final String ACTION_TEST_HANDOVER_FAIL = 95 "com.android.internal.telephony.TestHandoverFail"; 96 97 /** 98 * Test-only intent used to trigger signalling of a 99 * {@link com.android.internal.telephony.gsm.SuppServiceNotification} to the {@link ImsPhone}. 100 * Use {@link #EXTRA_CODE} to specify the 101 * {@link com.android.internal.telephony.gsm.SuppServiceNotification#code}. 102 */ 103 private static final String ACTION_TEST_SUPP_SRVC_NOTIFICATION = 104 "com.android.internal.telephony.TestSuppSrvcNotification"; 105 106 private static final String EXTRA_CODE = "code"; 107 private static final String EXTRA_TYPE = "type"; 108 109 /** 110 * Test-only intent used to trigger signalling that an IMS call is an emergency call. 111 */ 112 private static final String ACTION_TEST_IMS_E_CALL = 113 "com.android.internal.telephony.TestImsECall"; 114 115 /** 116 * Test-only intent used to trigger a change to the current call's phone number. 117 * Use the {@link #EXTRA_NUMBER} extra to specify the new phone number. 118 */ 119 private static final String ACTION_TEST_CHANGE_NUMBER = 120 "com.android.internal.telephony.TestChangeNumber"; 121 122 private static final String ACTION_TEST_SERVICE_STATE = 123 "com.android.internal.telephony.TestServiceState"; 124 125 private static final String EXTRA_ACTION = "action"; 126 private static final String EXTRA_VOICE_RAT = "voice_rat"; 127 private static final String EXTRA_DATA_RAT = "data_rat"; 128 private static final String EXTRA_VOICE_REG_STATE = "voice_reg_state"; 129 private static final String EXTRA_DATA_REG_STATE = "data_reg_state"; 130 private static final String EXTRA_VOICE_ROAMING_TYPE = "voice_roaming_type"; 131 private static final String EXTRA_DATA_ROAMING_TYPE = "data_roaming_type"; 132 private static final String EXTRA_OPERATOR = "operator"; 133 134 private static final String ACTION_RESET = "reset"; 135 136 private static List<ImsExternalCallState> mImsExternalCallStates = null; 137 138 private Intent mServiceStateTestIntent; 139 140 private Phone mPhone; 141 142 // The static intent receiver one for all instances and we assume this 143 // is running on the same thread as Dcc. 144 protected BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 145 @Override 146 public void onReceive(Context context, Intent intent) { 147 String action = intent.getAction(); 148 try { 149 if (DBG) log("sIntentReceiver.onReceive: action=" + action); 150 if (action.equals(mPhone.getActionDetached())) { 151 log("simulate detaching"); 152 mPhone.getServiceStateTracker().mDetachedRegistrants.get( 153 AccessNetworkConstants.TRANSPORT_TYPE_WWAN).notifyRegistrants(); 154 } else if (action.equals(mPhone.getActionAttached())) { 155 log("simulate attaching"); 156 mPhone.getServiceStateTracker().mAttachedRegistrants.get( 157 AccessNetworkConstants.TRANSPORT_TYPE_WWAN).notifyRegistrants(); 158 } else if (action.equals(ACTION_TEST_CONFERENCE_EVENT_PACKAGE)) { 159 log("inject simulated conference event package"); 160 handleTestConferenceEventPackage(context, 161 intent.getStringExtra(EXTRA_FILENAME)); 162 } else if (action.equals(ACTION_TEST_DIALOG_EVENT_PACKAGE)) { 163 log("handle test dialog event package intent"); 164 handleTestDialogEventPackageIntent(intent); 165 } else if (action.equals(ACTION_TEST_SUPP_SRVC_FAIL)) { 166 log("handle test supp svc failed intent"); 167 handleSuppServiceFailedIntent(intent); 168 } else if (action.equals(ACTION_TEST_HANDOVER_FAIL)) { 169 log("handle handover fail test intent"); 170 handleHandoverFailedIntent(); 171 } else if (action.equals(ACTION_TEST_SUPP_SRVC_NOTIFICATION)) { 172 log("handle supp service notification test intent"); 173 sendTestSuppServiceNotification(intent); 174 } else if (action.equals(ACTION_TEST_SERVICE_STATE)) { 175 log("handle test service state changed intent"); 176 // Trigger the service state update. The replacement will be done in 177 // overrideServiceState(). 178 mServiceStateTestIntent = intent; 179 mPhone.getServiceStateTracker().sendEmptyMessage( 180 ServiceStateTracker.EVENT_NETWORK_STATE_CHANGED); 181 } else if (action.equals(ACTION_TEST_IMS_E_CALL)) { 182 log("handle test IMS ecall intent"); 183 testImsECall(); 184 } else if (action.equals(ACTION_TEST_CHANGE_NUMBER)) { 185 log("handle test change number intent"); 186 testChangeNumber(intent); 187 } else { 188 if (DBG) log("onReceive: unknown action=" + action); 189 } 190 } catch (BadParcelableException e) { 191 Rlog.w(LOG_TAG, e); 192 } 193 } 194 }; 195 TelephonyTester(Phone phone)196 TelephonyTester(Phone phone) { 197 mPhone = phone; 198 199 if (Build.IS_DEBUGGABLE) { 200 IntentFilter filter = new IntentFilter(); 201 202 filter.addAction(mPhone.getActionDetached()); 203 log("register for intent action=" + mPhone.getActionDetached()); 204 205 filter.addAction(mPhone.getActionAttached()); 206 log("register for intent action=" + mPhone.getActionAttached()); 207 208 if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) { 209 log("register for intent action=" + ACTION_TEST_CONFERENCE_EVENT_PACKAGE); 210 filter.addAction(ACTION_TEST_CONFERENCE_EVENT_PACKAGE); 211 filter.addAction(ACTION_TEST_DIALOG_EVENT_PACKAGE); 212 filter.addAction(ACTION_TEST_SUPP_SRVC_FAIL); 213 filter.addAction(ACTION_TEST_HANDOVER_FAIL); 214 filter.addAction(ACTION_TEST_SUPP_SRVC_NOTIFICATION); 215 filter.addAction(ACTION_TEST_IMS_E_CALL); 216 mImsExternalCallStates = new ArrayList<ImsExternalCallState>(); 217 } else { 218 filter.addAction(ACTION_TEST_SERVICE_STATE); 219 log("register for intent action=" + ACTION_TEST_SERVICE_STATE); 220 } 221 filter.addAction(ACTION_TEST_CHANGE_NUMBER); 222 phone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone.getHandler()); 223 } 224 } 225 dispose()226 void dispose() { 227 if (Build.IS_DEBUGGABLE) { 228 mPhone.getContext().unregisterReceiver(mIntentReceiver); 229 } 230 } 231 log(String s)232 private static void log(String s) { 233 Rlog.d(LOG_TAG, s); 234 } 235 handleSuppServiceFailedIntent(Intent intent)236 private void handleSuppServiceFailedIntent(Intent intent) { 237 ImsPhone imsPhone = (ImsPhone) mPhone; 238 if (imsPhone == null) { 239 return; 240 } 241 int code = intent.getIntExtra(EXTRA_FAILURE_CODE, 0); 242 imsPhone.notifySuppServiceFailed(PhoneInternalInterface.SuppService.values()[code]); 243 } 244 handleHandoverFailedIntent()245 private void handleHandoverFailedIntent() { 246 // Attempt to get the active IMS call 247 ImsPhone imsPhone = (ImsPhone) mPhone; 248 if (imsPhone == null) { 249 return; 250 } 251 252 ImsPhoneCall imsPhoneCall = imsPhone.getForegroundCall(); 253 if (imsPhoneCall == null) { 254 return; 255 } 256 257 ImsCall imsCall = imsPhoneCall.getImsCall(); 258 if (imsCall == null) { 259 return; 260 } 261 262 imsCall.getImsCallSessionListenerProxy().callSessionHandoverFailed(imsCall.getCallSession(), 263 ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, 264 new ImsReasonInfo()); 265 } 266 267 /** 268 * Handles request to send a test conference event package to the active Ims call. 269 * 270 * @see com.android.internal.telephony.test.TestConferenceEventPackageParser 271 * @param context The context. 272 * @param fileName The name of the test conference event package file to read. 273 */ handleTestConferenceEventPackage(Context context, String fileName)274 private void handleTestConferenceEventPackage(Context context, String fileName) { 275 // Attempt to get the active IMS call before parsing the test XML file. 276 ImsPhone imsPhone = (ImsPhone) mPhone; 277 if (imsPhone == null) { 278 return; 279 } 280 281 ImsPhoneCall imsPhoneCall = imsPhone.getForegroundCall(); 282 if (imsPhoneCall == null) { 283 return; 284 } 285 286 ImsCall imsCall = imsPhoneCall.getImsCall(); 287 if (imsCall == null) { 288 return; 289 } 290 291 File packageFile = new File(context.getFilesDir(), fileName); 292 final FileInputStream is; 293 try { 294 is = new FileInputStream(packageFile); 295 } catch (FileNotFoundException ex) { 296 log("Test conference event package file not found: " + packageFile.getAbsolutePath()); 297 return; 298 } 299 300 TestConferenceEventPackageParser parser = new TestConferenceEventPackageParser(is); 301 ImsConferenceState imsConferenceState = parser.parse(); 302 if (imsConferenceState == null) { 303 return; 304 } 305 306 imsCall.conferenceStateUpdated(imsConferenceState); 307 } 308 309 /** 310 * Handles intents containing test dialog event package data. 311 * 312 * @param intent 313 */ handleTestDialogEventPackageIntent(Intent intent)314 private void handleTestDialogEventPackageIntent(Intent intent) { 315 ImsPhone imsPhone = (ImsPhone) mPhone; 316 if (imsPhone == null) { 317 return; 318 } 319 ImsExternalCallTracker externalCallTracker = imsPhone.getExternalCallTracker(); 320 if (externalCallTracker == null) { 321 return; 322 } 323 324 if (intent.hasExtra(EXTRA_STARTPACKAGE)) { 325 mImsExternalCallStates.clear(); 326 } else if (intent.hasExtra(EXTRA_SENDPACKAGE)) { 327 externalCallTracker.refreshExternalCallState(mImsExternalCallStates); 328 mImsExternalCallStates.clear(); 329 } else if (intent.hasExtra(EXTRA_DIALOGID)) { 330 ImsExternalCallState state = new ImsExternalCallState( 331 intent.getIntExtra(EXTRA_DIALOGID, 0), 332 Uri.parse(intent.getStringExtra(EXTRA_NUMBER)), 333 intent.getBooleanExtra(EXTRA_CANPULL, true), 334 intent.getIntExtra(EXTRA_STATE, 335 ImsExternalCallState.CALL_STATE_CONFIRMED), 336 ImsCallProfile.CALL_TYPE_VOICE, 337 false /* isHeld */ 338 ); 339 mImsExternalCallStates.add(state); 340 } 341 } 342 sendTestSuppServiceNotification(Intent intent)343 private void sendTestSuppServiceNotification(Intent intent) { 344 if (intent.hasExtra(EXTRA_CODE) && intent.hasExtra(EXTRA_TYPE)) { 345 int code = intent.getIntExtra(EXTRA_CODE, -1); 346 int type = intent.getIntExtra(EXTRA_TYPE, -1); 347 ImsPhone imsPhone = (ImsPhone) mPhone; 348 if (imsPhone == null) { 349 return; 350 } 351 log("Test supp service notification:" + code); 352 SuppServiceNotification suppServiceNotification = new SuppServiceNotification(); 353 suppServiceNotification.code = code; 354 suppServiceNotification.notificationType = type; 355 imsPhone.notifySuppSvcNotification(suppServiceNotification); 356 } 357 } 358 overrideServiceState(ServiceState ss)359 void overrideServiceState(ServiceState ss) { 360 if (mServiceStateTestIntent == null || ss == null) return; 361 if (mServiceStateTestIntent.hasExtra(EXTRA_ACTION) 362 && ACTION_RESET.equals(mServiceStateTestIntent.getStringExtra(EXTRA_ACTION))) { 363 log("Service state override reset"); 364 return; 365 } 366 367 // TODO: Fix this with modifing NetworkRegistrationInfo inside ServiceState. Do not call 368 // ServiceState's set methods directly. 369 /*if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_REG_STATE)) { 370 ss.setVoiceRegState(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_REG_STATE, 371 ServiceState.STATE_OUT_OF_SERVICE)); 372 log("Override voice service state with " + ss.getVoiceRegState()); 373 } 374 if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_REG_STATE)) { 375 ss.setDataRegState(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE, 376 ServiceState.STATE_OUT_OF_SERVICE)); 377 log("Override data service state with " + ss.getDataRegState()); 378 } 379 if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_RAT)) { 380 ss.setRilVoiceRadioTechnology(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_RAT, 381 ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)); 382 log("Override voice rat with " + ss.getRilVoiceRadioTechnology()); 383 } 384 if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_RAT)) { 385 ss.setRilDataRadioTechnology(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_RAT, 386 ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)); 387 log("Override data rat with " + ss.getRilDataRadioTechnology()); 388 }*/ 389 if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_ROAMING_TYPE)) { 390 ss.setVoiceRoamingType(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_ROAMING_TYPE, 391 ServiceState.ROAMING_TYPE_UNKNOWN)); 392 log("Override voice roaming type with " + ss.getVoiceRoamingType()); 393 } 394 if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_ROAMING_TYPE)) { 395 ss.setDataRoamingType(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_ROAMING_TYPE, 396 ServiceState.ROAMING_TYPE_UNKNOWN)); 397 log("Override data roaming type with " + ss.getDataRoamingType()); 398 } 399 if (mServiceStateTestIntent.hasExtra(EXTRA_OPERATOR)) { 400 String operator = mServiceStateTestIntent.getStringExtra(EXTRA_OPERATOR); 401 ss.setOperatorName(operator, operator, ""); 402 log("Override operator with " + operator); 403 } 404 } 405 testImsECall()406 void testImsECall() { 407 // Attempt to get the active IMS call before parsing the test XML file. 408 ImsPhone imsPhone = (ImsPhone) mPhone; 409 if (imsPhone == null) { 410 return; 411 } 412 413 ImsPhoneCall imsPhoneCall = imsPhone.getForegroundCall(); 414 if (imsPhoneCall == null) { 415 return; 416 } 417 418 ImsCall imsCall = imsPhoneCall.getImsCall(); 419 if (imsCall == null) { 420 return; 421 } 422 423 ImsCallProfile callProfile = imsCall.getCallProfile(); 424 Bundle extras = callProfile.getCallExtras(); 425 if (extras == null) { 426 extras = new Bundle(); 427 } 428 extras.putBoolean(ImsCallProfile.EXTRA_EMERGENCY_CALL, true); 429 callProfile.mCallExtras = extras; 430 imsCall.getImsCallSessionListenerProxy().callSessionUpdated(imsCall.getSession(), 431 callProfile); 432 } 433 testChangeNumber(Intent intent)434 void testChangeNumber(Intent intent) { 435 if (!intent.hasExtra(EXTRA_NUMBER)) { 436 return; 437 } 438 439 String newNumber = intent.getStringExtra(EXTRA_NUMBER); 440 441 // Update all the calls. 442 mPhone.getForegroundCall().getConnections() 443 .stream() 444 .forEach(c -> { 445 c.setAddress(newNumber, PhoneConstants.PRESENTATION_ALLOWED); 446 c.setDialString(newNumber); 447 }); 448 449 // <sigh> 450 if (mPhone instanceof GsmCdmaPhone) { 451 ((GsmCdmaPhone) mPhone).notifyPhoneStateChanged(); 452 ((GsmCdmaPhone) mPhone).notifyPreciseCallStateChanged(); 453 } else if (mPhone instanceof ImsPhone) { 454 ((ImsPhone) mPhone).notifyPhoneStateChanged(); 455 ((ImsPhone) mPhone).notifyPreciseCallStateChanged(); 456 } 457 } 458 }