1 /* 2 * Copyright (C) 2018 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.phone; 18 19 import android.content.Context; 20 import android.os.BasicShellCommandHandler; 21 import android.os.Binder; 22 import android.os.PersistableBundle; 23 import android.os.Process; 24 import android.os.RemoteException; 25 import android.telephony.CarrierConfigManager; 26 import android.telephony.SubscriptionInfo; 27 import android.telephony.SubscriptionManager; 28 import android.telephony.emergency.EmergencyNumber; 29 import android.telephony.ims.feature.ImsFeature; 30 import android.util.Log; 31 32 import com.android.internal.telephony.ITelephony; 33 import com.android.internal.telephony.emergency.EmergencyNumberTracker; 34 import com.android.internal.telephony.util.TelephonyUtils; 35 36 import java.io.PrintWriter; 37 import java.util.ArrayList; 38 import java.util.HashMap; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.TreeSet; 42 43 /** 44 * Takes actions based on the adb commands given by "adb shell cmd phone ...". Be careful, no 45 * permission checks have been done before onCommand was called. Make sure any commands processed 46 * here also contain the appropriate permissions checks. 47 */ 48 49 public class TelephonyShellCommand extends BasicShellCommandHandler { 50 51 private static final String LOG_TAG = "TelephonyShellCommand"; 52 // Don't commit with this true. 53 private static final boolean VDBG = true; 54 private static final int DEFAULT_PHONE_ID = 0; 55 56 private static final String IMS_SUBCOMMAND = "ims"; 57 private static final String NUMBER_VERIFICATION_SUBCOMMAND = "numverify"; 58 private static final String EMERGENCY_NUMBER_TEST_MODE = "emergency-number-test-mode"; 59 private static final String CARRIER_CONFIG_SUBCOMMAND = "cc"; 60 private static final String DATA_TEST_MODE = "data"; 61 private static final String DATA_ENABLE = "enable"; 62 private static final String DATA_DISABLE = "disable"; 63 64 private static final String IMS_SET_CARRIER_SERVICE = "set-ims-service"; 65 private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service"; 66 private static final String IMS_ENABLE = "enable"; 67 private static final String IMS_DISABLE = "disable"; 68 // Used to disable or enable processing of conference event package data from the network. 69 // This is handy for testing scenarios where CEP data does not exist on a network which does 70 // support CEP data. 71 private static final String IMS_CEP = "conference-event-package"; 72 73 private static final String NUMBER_VERIFICATION_OVERRIDE_PACKAGE = "override-package"; 74 private static final String NUMBER_VERIFICATION_FAKE_CALL = "fake-call"; 75 76 private static final String CC_GET_VALUE = "get-value"; 77 private static final String CC_SET_VALUE = "set-value"; 78 private static final String CC_CLEAR_VALUES = "clear-values"; 79 80 // Take advantage of existing methods that already contain permissions checks when possible. 81 private final ITelephony mInterface; 82 83 private SubscriptionManager mSubscriptionManager; 84 private CarrierConfigManager mCarrierConfigManager; 85 86 private enum CcType { 87 BOOLEAN, DOUBLE, DOUBLE_ARRAY, INT, INT_ARRAY, LONG, LONG_ARRAY, STRING, 88 STRING_ARRAY, UNKNOWN 89 } 90 91 private class CcOptionParseResult { 92 public int mSubId; 93 public boolean mPersistent; 94 } 95 96 // Maps carrier config keys to type. It is possible to infer the type for most carrier config 97 // keys by looking at the end of the string which usually tells the type. 98 // For instance: "xxxx_string", "xxxx_string_array", etc. 99 // The carrier config keys in this map does not follow this convention. It is therefore not 100 // possible to infer the type for these keys by looking at the string. 101 private static final Map<String, CcType> CC_TYPE_MAP = new HashMap<String, CcType>() {{ 102 put(CarrierConfigManager.Gps.KEY_A_GLONASS_POS_PROTOCOL_SELECT_STRING, CcType.STRING); 103 put(CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, CcType.STRING); 104 put(CarrierConfigManager.Gps.KEY_GPS_LOCK_STRING, CcType.STRING); 105 put(CarrierConfigManager.Gps.KEY_LPP_PROFILE_STRING, CcType.STRING); 106 put(CarrierConfigManager.Gps.KEY_NFW_PROXY_APPS_STRING, CcType.STRING); 107 put(CarrierConfigManager.Gps.KEY_SUPL_ES_STRING, CcType.STRING); 108 put(CarrierConfigManager.Gps.KEY_SUPL_HOST_STRING, CcType.STRING); 109 put(CarrierConfigManager.Gps.KEY_SUPL_MODE_STRING, CcType.STRING); 110 put(CarrierConfigManager.Gps.KEY_SUPL_PORT_STRING, CcType.STRING); 111 put(CarrierConfigManager.Gps.KEY_SUPL_VER_STRING, CcType.STRING); 112 put(CarrierConfigManager.Gps.KEY_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL_STRING, 113 CcType.STRING); 114 put(CarrierConfigManager.KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY, 115 CcType.STRING_ARRAY); 116 put(CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY, 117 CcType.STRING_ARRAY); 118 put(CarrierConfigManager.KEY_CARRIER_CALL_SCREENING_APP_STRING, CcType.STRING); 119 put(CarrierConfigManager.KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING, CcType.STRING); 120 put(CarrierConfigManager.KEY_MMS_HTTP_PARAMS_STRING, CcType.STRING); 121 put(CarrierConfigManager.KEY_MMS_NAI_SUFFIX_STRING, CcType.STRING); 122 put(CarrierConfigManager.KEY_MMS_UA_PROF_TAG_NAME_STRING, CcType.STRING); 123 put(CarrierConfigManager.KEY_MMS_UA_PROF_URL_STRING, CcType.STRING); 124 put(CarrierConfigManager.KEY_MMS_USER_AGENT_STRING, CcType.STRING); 125 put(CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES, CcType.STRING_ARRAY); 126 } 127 }; 128 TelephonyShellCommand(ITelephony binder, Context context)129 public TelephonyShellCommand(ITelephony binder, Context context) { 130 mInterface = binder; 131 mCarrierConfigManager = 132 (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 133 mSubscriptionManager = (SubscriptionManager) 134 context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); 135 } 136 137 @Override onCommand(String cmd)138 public int onCommand(String cmd) { 139 if (cmd == null) { 140 return handleDefaultCommands(null); 141 } 142 143 switch (cmd) { 144 case IMS_SUBCOMMAND: { 145 return handleImsCommand(); 146 } 147 case NUMBER_VERIFICATION_SUBCOMMAND: 148 return handleNumberVerificationCommand(); 149 case EMERGENCY_NUMBER_TEST_MODE: 150 return handleEmergencyNumberTestModeCommand(); 151 case CARRIER_CONFIG_SUBCOMMAND: { 152 return handleCcCommand(); 153 } 154 case DATA_TEST_MODE: 155 return handleDataTestModeCommand(); 156 default: { 157 return handleDefaultCommands(cmd); 158 } 159 } 160 } 161 162 @Override onHelp()163 public void onHelp() { 164 PrintWriter pw = getOutPrintWriter(); 165 pw.println("Telephony Commands:"); 166 pw.println(" help"); 167 pw.println(" Print this help text."); 168 pw.println(" ims"); 169 pw.println(" IMS Commands."); 170 pw.println(" emergency-number-test-mode"); 171 pw.println(" Emergency Number Test Mode Commands."); 172 pw.println(" data"); 173 pw.println(" Data Test Mode Commands."); 174 pw.println(" cc"); 175 pw.println(" Carrier Config Commands."); 176 onHelpIms(); 177 onHelpEmergencyNumber(); 178 onHelpDataTestMode(); 179 onHelpCc(); 180 } 181 onHelpIms()182 private void onHelpIms() { 183 PrintWriter pw = getOutPrintWriter(); 184 pw.println("IMS Commands:"); 185 pw.println(" ims set-ims-service [-s SLOT_ID] (-c | -d | -f) PACKAGE_NAME"); 186 pw.println(" Sets the ImsService defined in PACKAGE_NAME to to be the bound"); 187 pw.println(" ImsService. Options are:"); 188 pw.println(" -s: the slot ID that the ImsService should be bound for. If no option"); 189 pw.println(" is specified, it will choose the default voice SIM slot."); 190 pw.println(" -c: Override the ImsService defined in the carrier configuration."); 191 pw.println(" -d: Override the ImsService defined in the device overlay."); 192 pw.println(" -f: Set the feature that this override if for, if no option is"); 193 pw.println(" specified, the new package name will be used for all features."); 194 pw.println(" ims get-ims-service [-s SLOT_ID] [-c | -d]"); 195 pw.println(" Gets the package name of the currently defined ImsService."); 196 pw.println(" Options are:"); 197 pw.println(" -s: The SIM slot ID for the registered ImsService. If no option"); 198 pw.println(" is specified, it will choose the default voice SIM slot."); 199 pw.println(" -c: The ImsService defined as the carrier configured ImsService."); 200 pw.println(" -c: The ImsService defined as the device default ImsService."); 201 pw.println(" -f: The feature type that the query will be requested for. If none is"); 202 pw.println(" specified, the returned package name will correspond to MMTEL."); 203 pw.println(" ims enable [-s SLOT_ID]"); 204 pw.println(" enables IMS for the SIM slot specified, or for the default voice SIM slot"); 205 pw.println(" if none is specified."); 206 pw.println(" ims disable [-s SLOT_ID]"); 207 pw.println(" disables IMS for the SIM slot specified, or for the default voice SIM"); 208 pw.println(" slot if none is specified."); 209 pw.println(" ims conference-event-package [enable/disable]"); 210 pw.println(" enables or disables handling or network conference event package data."); 211 } 212 onHelpNumberVerification()213 private void onHelpNumberVerification() { 214 PrintWriter pw = getOutPrintWriter(); 215 pw.println("Number verification commands"); 216 pw.println(" numverify override-package PACKAGE_NAME;"); 217 pw.println(" Set the authorized package for number verification."); 218 pw.println(" Leave the package name blank to reset."); 219 pw.println(" numverify fake-call NUMBER;"); 220 pw.println(" Fake an incoming call from NUMBER. This is for testing. Output will be"); 221 pw.println(" 1 if the call would have been intercepted, 0 otherwise."); 222 } 223 onHelpDataTestMode()224 private void onHelpDataTestMode() { 225 PrintWriter pw = getOutPrintWriter(); 226 pw.println("Mobile Data Test Mode Commands:"); 227 pw.println(" data enable: enable mobile data connectivity"); 228 pw.println(" data disable: disable mobile data connectivity"); 229 } 230 onHelpEmergencyNumber()231 private void onHelpEmergencyNumber() { 232 PrintWriter pw = getOutPrintWriter(); 233 pw.println("Emergency Number Test Mode Commands:"); 234 pw.println(" emergency-number-test-mode "); 235 pw.println(" Add(-a), Clear(-c), Print (-p) or Remove(-r) the emergency number list in" 236 + " the test mode"); 237 pw.println(" -a <emergency number address>: add an emergency number address for the" 238 + " test mode, only allows '0'-'9', '*', '#' or '+'."); 239 pw.println(" -c: clear the emergency number list in the test mode."); 240 pw.println(" -r <emergency number address>: remove an existing emergency number" 241 + " address added by the test mode, only allows '0'-'9', '*', '#' or '+'."); 242 pw.println(" -p: get the full emergency number list in the test mode."); 243 } 244 onHelpCc()245 private void onHelpCc() { 246 PrintWriter pw = getOutPrintWriter(); 247 pw.println("Carrier Config Commands:"); 248 pw.println(" cc get-value [-s SLOT_ID] [KEY]"); 249 pw.println(" Print carrier config values."); 250 pw.println(" Options are:"); 251 pw.println(" -s: The SIM slot ID to read carrier config value for. If no option"); 252 pw.println(" is specified, it will choose the default voice SIM slot."); 253 pw.println(" KEY: The key to the carrier config value to print. All values are printed"); 254 pw.println(" if KEY is not specified."); 255 pw.println(" cc set-value [-s SLOT_ID] [-p] KEY [NEW_VALUE]"); 256 pw.println(" Set carrier config KEY to NEW_VALUE."); 257 pw.println(" Options are:"); 258 pw.println(" -s: The SIM slot ID to set carrier config value for. If no option"); 259 pw.println(" is specified, it will choose the default voice SIM slot."); 260 pw.println(" -p: Value will be stored persistent"); 261 pw.println(" NEW_VALUE specifies the new value for carrier config KEY. Null will be"); 262 pw.println(" used if NEW_VALUE is not set. Strings should be encapsulated with"); 263 pw.println(" quotation marks. Spaces needs to be escaped. Example: \"Hello\\ World\""); 264 pw.println(" Separate items in arrays with space . Example: \"item1\" \"item2\""); 265 pw.println(" cc clear-values [-s SLOT_ID]"); 266 pw.println(" Clear all carrier override values that has previously been set"); 267 pw.println(" with set-value"); 268 pw.println(" Options are:"); 269 pw.println(" -s: The SIM slot ID to clear carrier config values for. If no option"); 270 pw.println(" is specified, it will choose the default voice SIM slot."); 271 } 272 handleImsCommand()273 private int handleImsCommand() { 274 String arg = getNextArg(); 275 if (arg == null) { 276 onHelpIms(); 277 return 0; 278 } 279 280 switch (arg) { 281 case IMS_SET_CARRIER_SERVICE: { 282 return handleImsSetServiceCommand(); 283 } 284 case IMS_GET_CARRIER_SERVICE: { 285 return handleImsGetServiceCommand(); 286 } 287 case IMS_ENABLE: { 288 return handleEnableIms(); 289 } 290 case IMS_DISABLE: { 291 return handleDisableIms(); 292 } 293 case IMS_CEP: { 294 return handleCepChange(); 295 } 296 } 297 298 return -1; 299 } 300 handleDataTestModeCommand()301 private int handleDataTestModeCommand() { 302 PrintWriter errPw = getErrPrintWriter(); 303 String arg = getNextArgRequired(); 304 if (arg == null) { 305 onHelpDataTestMode(); 306 return 0; 307 } 308 switch (arg) { 309 case DATA_ENABLE: { 310 try { 311 mInterface.enableDataConnectivity(); 312 } catch (RemoteException ex) { 313 Log.w(LOG_TAG, "data enable, error " + ex.getMessage()); 314 errPw.println("Exception: " + ex.getMessage()); 315 return -1; 316 } 317 break; 318 } 319 case DATA_DISABLE: { 320 try { 321 mInterface.disableDataConnectivity(); 322 } catch (RemoteException ex) { 323 Log.w(LOG_TAG, "data disable, error " + ex.getMessage()); 324 errPw.println("Exception: " + ex.getMessage()); 325 return -1; 326 } 327 break; 328 } 329 default: 330 onHelpDataTestMode(); 331 break; 332 } 333 return 0; 334 } 335 handleEmergencyNumberTestModeCommand()336 private int handleEmergencyNumberTestModeCommand() { 337 PrintWriter errPw = getErrPrintWriter(); 338 String opt = getNextOption(); 339 if (opt == null) { 340 onHelpEmergencyNumber(); 341 return 0; 342 } 343 344 switch (opt) { 345 case "-a": { 346 String emergencyNumberCmd = getNextArgRequired(); 347 if (emergencyNumberCmd == null 348 || !EmergencyNumber.validateEmergencyNumberAddress(emergencyNumberCmd)) { 349 errPw.println("An emergency number (only allow '0'-'9', '*', '#' or '+') needs" 350 + " to be specified after -a in the command "); 351 return -1; 352 } 353 try { 354 mInterface.updateEmergencyNumberListTestMode( 355 EmergencyNumberTracker.ADD_EMERGENCY_NUMBER_TEST_MODE, 356 new EmergencyNumber(emergencyNumberCmd, "", "", 357 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED, 358 new ArrayList<String>(), 359 EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST, 360 EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN)); 361 } catch (RemoteException ex) { 362 Log.w(LOG_TAG, "emergency-number-test-mode -a " + emergencyNumberCmd 363 + ", error " + ex.getMessage()); 364 errPw.println("Exception: " + ex.getMessage()); 365 return -1; 366 } 367 break; 368 } 369 case "-c": { 370 try { 371 mInterface.updateEmergencyNumberListTestMode( 372 EmergencyNumberTracker.RESET_EMERGENCY_NUMBER_TEST_MODE, null); 373 } catch (RemoteException ex) { 374 Log.w(LOG_TAG, "emergency-number-test-mode -c " + "error " + ex.getMessage()); 375 errPw.println("Exception: " + ex.getMessage()); 376 return -1; 377 } 378 break; 379 } 380 case "-r": { 381 String emergencyNumberCmd = getNextArgRequired(); 382 if (emergencyNumberCmd == null 383 || !EmergencyNumber.validateEmergencyNumberAddress(emergencyNumberCmd)) { 384 errPw.println("An emergency number (only allow '0'-'9', '*', '#' or '+') needs" 385 + " to be specified after -r in the command "); 386 return -1; 387 } 388 try { 389 mInterface.updateEmergencyNumberListTestMode( 390 EmergencyNumberTracker.REMOVE_EMERGENCY_NUMBER_TEST_MODE, 391 new EmergencyNumber(emergencyNumberCmd, "", "", 392 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED, 393 new ArrayList<String>(), 394 EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST, 395 EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN)); 396 } catch (RemoteException ex) { 397 Log.w(LOG_TAG, "emergency-number-test-mode -r " + emergencyNumberCmd 398 + ", error " + ex.getMessage()); 399 errPw.println("Exception: " + ex.getMessage()); 400 return -1; 401 } 402 break; 403 } 404 case "-p": { 405 try { 406 getOutPrintWriter().println(mInterface.getEmergencyNumberListTestMode()); 407 } catch (RemoteException ex) { 408 Log.w(LOG_TAG, "emergency-number-test-mode -p " + "error " + ex.getMessage()); 409 errPw.println("Exception: " + ex.getMessage()); 410 return -1; 411 } 412 break; 413 } 414 default: 415 onHelpEmergencyNumber(); 416 break; 417 } 418 return 0; 419 } 420 handleNumberVerificationCommand()421 private int handleNumberVerificationCommand() { 422 String arg = getNextArg(); 423 if (arg == null) { 424 onHelpNumberVerification(); 425 return 0; 426 } 427 428 if (!checkShellUid()) { 429 return -1; 430 } 431 432 switch (arg) { 433 case NUMBER_VERIFICATION_OVERRIDE_PACKAGE: { 434 NumberVerificationManager.overrideAuthorizedPackage(getNextArg()); 435 return 0; 436 } 437 case NUMBER_VERIFICATION_FAKE_CALL: { 438 boolean val = NumberVerificationManager.getInstance() 439 .checkIncomingCall(getNextArg()); 440 getOutPrintWriter().println(val ? "1" : "0"); 441 return 0; 442 } 443 } 444 445 return -1; 446 } 447 448 // ims set-ims-service handleImsSetServiceCommand()449 private int handleImsSetServiceCommand() { 450 PrintWriter errPw = getErrPrintWriter(); 451 int slotId = getDefaultSlot(); 452 Boolean isCarrierService = null; 453 List<Integer> featuresList = new ArrayList<>(); 454 455 String opt; 456 while ((opt = getNextOption()) != null) { 457 switch (opt) { 458 case "-s": { 459 try { 460 slotId = Integer.parseInt(getNextArgRequired()); 461 } catch (NumberFormatException e) { 462 errPw.println("ims set-ims-service requires an integer as a SLOT_ID."); 463 return -1; 464 } 465 break; 466 } 467 case "-c": { 468 isCarrierService = true; 469 break; 470 } 471 case "-d": { 472 isCarrierService = false; 473 break; 474 } 475 case "-f": { 476 String featureString = getNextArgRequired(); 477 String[] features = featureString.split(","); 478 for (int i = 0; i < features.length; i++) { 479 try { 480 Integer result = Integer.parseInt(features[i]); 481 if (result < ImsFeature.FEATURE_EMERGENCY_MMTEL 482 || result >= ImsFeature.FEATURE_MAX) { 483 errPw.println("ims set-ims-service -f " + result 484 + " is an invalid feature."); 485 return -1; 486 } 487 featuresList.add(result); 488 } catch (NumberFormatException e) { 489 errPw.println("ims set-ims-service -f tried to parse " + features[i] 490 + " as an integer."); 491 return -1; 492 } 493 } 494 } 495 } 496 } 497 // Mandatory param, either -c or -d 498 if (isCarrierService == null) { 499 errPw.println("ims set-ims-service requires either \"-c\" or \"-d\" to be set."); 500 return -1; 501 } 502 503 String packageName = getNextArg(); 504 505 try { 506 if (packageName == null) { 507 packageName = ""; 508 } 509 int[] featureArray = new int[featuresList.size()]; 510 for (int i = 0; i < featuresList.size(); i++) { 511 featureArray[i] = featuresList.get(i); 512 } 513 boolean result = mInterface.setBoundImsServiceOverride(slotId, isCarrierService, 514 featureArray, packageName); 515 if (VDBG) { 516 Log.v(LOG_TAG, "ims set-ims-service -s " + slotId + " " 517 + (isCarrierService ? "-c " : "-d ") 518 + "-f " + featuresList + " " 519 + packageName + ", result=" + result); 520 } 521 getOutPrintWriter().println(result); 522 } catch (RemoteException e) { 523 Log.w(LOG_TAG, "ims set-ims-service -s " + slotId + " " 524 + (isCarrierService ? "-c " : "-d ") 525 + "-f " + featuresList + " " 526 + packageName + ", error" + e.getMessage()); 527 errPw.println("Exception: " + e.getMessage()); 528 return -1; 529 } 530 return 0; 531 } 532 533 // ims get-ims-service handleImsGetServiceCommand()534 private int handleImsGetServiceCommand() { 535 PrintWriter errPw = getErrPrintWriter(); 536 int slotId = getDefaultSlot(); 537 Boolean isCarrierService = null; 538 Integer featureType = ImsFeature.FEATURE_MMTEL; 539 540 String opt; 541 while ((opt = getNextOption()) != null) { 542 switch (opt) { 543 case "-s": { 544 try { 545 slotId = Integer.parseInt(getNextArgRequired()); 546 } catch (NumberFormatException e) { 547 errPw.println("ims set-ims-service requires an integer as a SLOT_ID."); 548 return -1; 549 } 550 break; 551 } 552 case "-c": { 553 isCarrierService = true; 554 break; 555 } 556 case "-d": { 557 isCarrierService = false; 558 break; 559 } 560 case "-f": { 561 try { 562 featureType = Integer.parseInt(getNextArg()); 563 } catch (NumberFormatException e) { 564 errPw.println("ims get-ims-service -f requires valid integer as feature."); 565 return -1; 566 } 567 if (featureType < ImsFeature.FEATURE_EMERGENCY_MMTEL 568 || featureType >= ImsFeature.FEATURE_MAX) { 569 errPw.println("ims get-ims-service -f invalid feature."); 570 return -1; 571 } 572 } 573 } 574 } 575 // Mandatory param, either -c or -d 576 if (isCarrierService == null) { 577 errPw.println("ims get-ims-service requires either \"-c\" or \"-d\" to be set."); 578 return -1; 579 } 580 581 String result; 582 try { 583 result = mInterface.getBoundImsServicePackage(slotId, isCarrierService, featureType); 584 } catch (RemoteException e) { 585 return -1; 586 } 587 if (VDBG) { 588 Log.v(LOG_TAG, "ims get-ims-service -s " + slotId + " " 589 + (isCarrierService ? "-c " : "-d ") 590 + (featureType != null ? ("-f " + featureType) : "") + " , returned: " 591 + result); 592 } 593 getOutPrintWriter().println(result); 594 return 0; 595 } 596 handleEnableIms()597 private int handleEnableIms() { 598 int slotId = getDefaultSlot(); 599 String opt; 600 while ((opt = getNextOption()) != null) { 601 switch (opt) { 602 case "-s": { 603 try { 604 slotId = Integer.parseInt(getNextArgRequired()); 605 } catch (NumberFormatException e) { 606 getErrPrintWriter().println("ims enable requires an integer as a SLOT_ID."); 607 return -1; 608 } 609 break; 610 } 611 } 612 } 613 try { 614 mInterface.enableIms(slotId); 615 } catch (RemoteException e) { 616 return -1; 617 } 618 if (VDBG) { 619 Log.v(LOG_TAG, "ims enable -s " + slotId); 620 } 621 return 0; 622 } 623 handleDisableIms()624 private int handleDisableIms() { 625 int slotId = getDefaultSlot(); 626 String opt; 627 while ((opt = getNextOption()) != null) { 628 switch (opt) { 629 case "-s": { 630 try { 631 slotId = Integer.parseInt(getNextArgRequired()); 632 } catch (NumberFormatException e) { 633 getErrPrintWriter().println( 634 "ims disable requires an integer as a SLOT_ID."); 635 return -1; 636 } 637 break; 638 } 639 } 640 } 641 try { 642 mInterface.disableIms(slotId); 643 } catch (RemoteException e) { 644 return -1; 645 } 646 if (VDBG) { 647 Log.v(LOG_TAG, "ims disable -s " + slotId); 648 } 649 return 0; 650 } 651 handleCepChange()652 private int handleCepChange() { 653 Log.i(LOG_TAG, "handleCepChange"); 654 String opt = getNextArg(); 655 if (opt == null) { 656 return -1; 657 } 658 boolean isCepEnabled = opt.equals("enable"); 659 660 try { 661 mInterface.setCepEnabled(isCepEnabled); 662 } catch (RemoteException e) { 663 return -1; 664 } 665 return 0; 666 } 667 getDefaultSlot()668 private int getDefaultSlot() { 669 int slotId = SubscriptionManager.getDefaultVoicePhoneId(); 670 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX 671 || slotId == SubscriptionManager.DEFAULT_PHONE_INDEX) { 672 // If there is no default, default to slot 0. 673 slotId = DEFAULT_PHONE_ID; 674 } 675 return slotId; 676 } 677 678 // Parse options related to Carrier Config Commands. parseCcOptions(String tag, boolean allowOptionPersistent)679 private CcOptionParseResult parseCcOptions(String tag, boolean allowOptionPersistent) { 680 PrintWriter errPw = getErrPrintWriter(); 681 CcOptionParseResult result = new CcOptionParseResult(); 682 result.mSubId = SubscriptionManager.getDefaultSubscriptionId(); 683 result.mPersistent = false; 684 685 String opt; 686 while ((opt = getNextOption()) != null) { 687 switch (opt) { 688 case "-s": { 689 try { 690 result.mSubId = slotStringToSubId(tag, getNextArgRequired()); 691 if (!SubscriptionManager.isValidSubscriptionId(result.mSubId)) { 692 errPw.println(tag + "No valid subscription found."); 693 return null; 694 } 695 696 } catch (IllegalArgumentException e) { 697 // Missing slot id 698 errPw.println(tag + "SLOT_ID expected after -s."); 699 return null; 700 } 701 break; 702 } 703 case "-p": { 704 if (allowOptionPersistent) { 705 result.mPersistent = true; 706 } else { 707 errPw.println(tag + "Unexpected option " + opt); 708 return null; 709 } 710 break; 711 } 712 default: { 713 errPw.println(tag + "Unknown option " + opt); 714 return null; 715 } 716 } 717 } 718 return result; 719 } 720 slotStringToSubId(String tag, String slotString)721 private int slotStringToSubId(String tag, String slotString) { 722 int slotId = -1; 723 try { 724 slotId = Integer.parseInt(slotString); 725 } catch (NumberFormatException e) { 726 getErrPrintWriter().println(tag + slotString + " is not a valid SLOT_ID."); 727 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 728 } 729 730 SubscriptionInfo subInfo = 731 mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(slotId); 732 if (subInfo == null) { 733 getErrPrintWriter().println(tag + "No subscription found in slot " + slotId + "."); 734 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 735 } 736 return subInfo.getSubscriptionId(); 737 } 738 checkShellUid()739 private boolean checkShellUid() { 740 // adb can run as root or as shell, depending on whether the device is rooted. 741 return Binder.getCallingUid() == Process.SHELL_UID 742 || Binder.getCallingUid() == Process.ROOT_UID; 743 } 744 handleCcCommand()745 private int handleCcCommand() { 746 // Verify that the user is allowed to run the command. Only allowed in rooted device in a 747 // non user build. 748 if (Binder.getCallingUid() != Process.ROOT_UID || TelephonyUtils.IS_USER) { 749 getErrPrintWriter().println("cc: Permission denied."); 750 return -1; 751 } 752 753 String arg = getNextArg(); 754 if (arg == null) { 755 onHelpCc(); 756 return 0; 757 } 758 759 switch (arg) { 760 case CC_GET_VALUE: { 761 return handleCcGetValue(); 762 } 763 case CC_SET_VALUE: { 764 return handleCcSetValue(); 765 } 766 case CC_CLEAR_VALUES: { 767 return handleCcClearValues(); 768 } 769 default: { 770 getErrPrintWriter().println("cc: Unknown argument: " + arg); 771 } 772 } 773 return -1; 774 } 775 776 // cc get-value handleCcGetValue()777 private int handleCcGetValue() { 778 PrintWriter errPw = getErrPrintWriter(); 779 String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_GET_VALUE + ": "; 780 String key = null; 781 782 // Parse all options 783 CcOptionParseResult options = parseCcOptions(tag, false); 784 if (options == null) { 785 return -1; 786 } 787 788 // Get bundle containing all carrier configuration values. 789 PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(options.mSubId); 790 if (bundle == null) { 791 errPw.println(tag + "No carrier config values found for subId " + options.mSubId + "."); 792 return -1; 793 } 794 795 // Get the key. 796 key = getNextArg(); 797 if (key != null) { 798 // A key was provided. Verify if it is a valid key 799 if (!bundle.containsKey(key)) { 800 errPw.println(tag + key + " is not a valid key."); 801 return -1; 802 } 803 804 // Print the carrier config value for key. 805 getOutPrintWriter().println(ccValueToString(key, getType(tag, key, bundle), bundle)); 806 } else { 807 // No key provided. Show all values. 808 // Iterate over a sorted list of all carrier config keys and print them. 809 TreeSet<String> sortedSet = new TreeSet<String>(bundle.keySet()); 810 for (String k : sortedSet) { 811 getOutPrintWriter().println(ccValueToString(k, getType(tag, k, bundle), bundle)); 812 } 813 } 814 return 0; 815 } 816 817 // cc set-value handleCcSetValue()818 private int handleCcSetValue() { 819 PrintWriter errPw = getErrPrintWriter(); 820 String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_SET_VALUE + ": "; 821 822 // Parse all options 823 CcOptionParseResult options = parseCcOptions(tag, true); 824 if (options == null) { 825 return -1; 826 } 827 828 // Get bundle containing all current carrier configuration values. 829 PersistableBundle originalValues = mCarrierConfigManager.getConfigForSubId(options.mSubId); 830 if (originalValues == null) { 831 errPw.println(tag + "No carrier config values found for subId " + options.mSubId + "."); 832 return -1; 833 } 834 835 // Get the key. 836 String key = getNextArg(); 837 if (key == null || key.equals("")) { 838 errPw.println(tag + "KEY is missing"); 839 return -1; 840 } 841 842 // Verify if the key is valid 843 if (!originalValues.containsKey(key)) { 844 errPw.println(tag + key + " is not a valid key."); 845 return -1; 846 } 847 848 // Remaining arguments is a list of new values. Add them all into an ArrayList. 849 ArrayList<String> valueList = new ArrayList<String>(); 850 while (peekNextArg() != null) { 851 valueList.add(getNextArg()); 852 } 853 854 // Find the type of the carrier config value 855 CcType type = getType(tag, key, originalValues); 856 if (type == CcType.UNKNOWN) { 857 errPw.println(tag + "ERROR: Not possible to override key with unknown type."); 858 return -1; 859 } 860 861 // Create an override bundle containing the key and value that should be overriden. 862 PersistableBundle overrideBundle = getOverrideBundle(tag, type, key, valueList); 863 if (overrideBundle == null) { 864 return -1; 865 } 866 867 // Override the value 868 mCarrierConfigManager.overrideConfig(options.mSubId, overrideBundle, options.mPersistent); 869 870 // Find bundle containing all new carrier configuration values after the override. 871 PersistableBundle newValues = mCarrierConfigManager.getConfigForSubId(options.mSubId); 872 if (newValues == null) { 873 errPw.println(tag + "No carrier config values found for subId " + options.mSubId + "."); 874 return -1; 875 } 876 877 // Print the original and new value. 878 String originalValueString = ccValueToString(key, type, originalValues); 879 String newValueString = ccValueToString(key, type, newValues); 880 getOutPrintWriter().println("Previous value: \n" + originalValueString); 881 getOutPrintWriter().println("New value: \n" + newValueString); 882 883 return 0; 884 } 885 886 // cc clear-values handleCcClearValues()887 private int handleCcClearValues() { 888 PrintWriter errPw = getErrPrintWriter(); 889 String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_CLEAR_VALUES + ": "; 890 891 // Parse all options 892 CcOptionParseResult options = parseCcOptions(tag, false); 893 if (options == null) { 894 return -1; 895 } 896 897 // Clear all values that has previously been set. 898 mCarrierConfigManager.overrideConfig(options.mSubId, null, true); 899 getOutPrintWriter() 900 .println("All previously set carrier config override values has been cleared"); 901 return 0; 902 } 903 getType(String tag, String key, PersistableBundle bundle)904 private CcType getType(String tag, String key, PersistableBundle bundle) { 905 // Find the type by checking the type of the current value stored in the bundle. 906 Object value = bundle.get(key); 907 908 if (CC_TYPE_MAP.containsKey(key)) { 909 return CC_TYPE_MAP.get(key); 910 } else if (value != null) { 911 if (value instanceof Boolean) { 912 return CcType.BOOLEAN; 913 } else if (value instanceof Double) { 914 return CcType.DOUBLE; 915 } else if (value instanceof double[]) { 916 return CcType.DOUBLE_ARRAY; 917 } else if (value instanceof Integer) { 918 return CcType.INT; 919 } else if (value instanceof int[]) { 920 return CcType.INT_ARRAY; 921 } else if (value instanceof Long) { 922 return CcType.LONG; 923 } else if (value instanceof long[]) { 924 return CcType.LONG_ARRAY; 925 } else if (value instanceof String) { 926 return CcType.STRING; 927 } else if (value instanceof String[]) { 928 return CcType.STRING_ARRAY; 929 } 930 } else { 931 // Current value was null and can therefore not be used in order to find the type. 932 // Check the name of the key to infer the type. This check is not needed for primitive 933 // data types (boolean, double, int and long), since they can not be null. 934 if (key.endsWith("double_array")) { 935 return CcType.DOUBLE_ARRAY; 936 } 937 if (key.endsWith("int_array")) { 938 return CcType.INT_ARRAY; 939 } 940 if (key.endsWith("long_array")) { 941 return CcType.LONG_ARRAY; 942 } 943 if (key.endsWith("string")) { 944 return CcType.STRING; 945 } 946 if (key.endsWith("string_array") || key.endsWith("strings")) { 947 return CcType.STRING_ARRAY; 948 } 949 } 950 951 // Not possible to infer the type by looking at the current value or the key. 952 PrintWriter errPw = getErrPrintWriter(); 953 errPw.println(tag + "ERROR: " + key + " has unknown type."); 954 return CcType.UNKNOWN; 955 } 956 ccValueToString(String key, CcType type, PersistableBundle bundle)957 private String ccValueToString(String key, CcType type, PersistableBundle bundle) { 958 String result; 959 StringBuilder valueString = new StringBuilder(); 960 String typeString = type.toString(); 961 Object value = bundle.get(key); 962 963 if (value == null) { 964 valueString.append("null"); 965 } else { 966 switch (type) { 967 case DOUBLE_ARRAY: { 968 // Format the string representation of the int array as value1 value2...... 969 double[] valueArray = (double[]) value; 970 for (int i = 0; i < valueArray.length; i++) { 971 if (i != 0) { 972 valueString.append(" "); 973 } 974 valueString.append(valueArray[i]); 975 } 976 break; 977 } 978 case INT_ARRAY: { 979 // Format the string representation of the int array as value1 value2...... 980 int[] valueArray = (int[]) value; 981 for (int i = 0; i < valueArray.length; i++) { 982 if (i != 0) { 983 valueString.append(" "); 984 } 985 valueString.append(valueArray[i]); 986 } 987 break; 988 } 989 case LONG_ARRAY: { 990 // Format the string representation of the int array as value1 value2...... 991 long[] valueArray = (long[]) value; 992 for (int i = 0; i < valueArray.length; i++) { 993 if (i != 0) { 994 valueString.append(" "); 995 } 996 valueString.append(valueArray[i]); 997 } 998 break; 999 } 1000 case STRING: { 1001 valueString.append("\"" + value.toString() + "\""); 1002 break; 1003 } 1004 case STRING_ARRAY: { 1005 // Format the string representation of the string array as "value1" "value2".... 1006 String[] valueArray = (String[]) value; 1007 for (int i = 0; i < valueArray.length; i++) { 1008 if (i != 0) { 1009 valueString.append(" "); 1010 } 1011 if (valueArray[i] != null) { 1012 valueString.append("\"" + valueArray[i] + "\""); 1013 } else { 1014 valueString.append("null"); 1015 } 1016 } 1017 break; 1018 } 1019 default: { 1020 valueString.append(value.toString()); 1021 } 1022 } 1023 } 1024 return String.format("%-70s %-15s %s", key, typeString, valueString); 1025 } 1026 getOverrideBundle(String tag, CcType type, String key, ArrayList<String> valueList)1027 private PersistableBundle getOverrideBundle(String tag, CcType type, String key, 1028 ArrayList<String> valueList) { 1029 PrintWriter errPw = getErrPrintWriter(); 1030 PersistableBundle bundle = new PersistableBundle(); 1031 1032 // First verify that a valid number of values has been provided for the type. 1033 switch (type) { 1034 case BOOLEAN: 1035 case DOUBLE: 1036 case INT: 1037 case LONG: { 1038 if (valueList.size() != 1) { 1039 errPw.println(tag + "Expected 1 value for type " + type 1040 + ". Found: " + valueList.size()); 1041 return null; 1042 } 1043 break; 1044 } 1045 case STRING: { 1046 if (valueList.size() > 1) { 1047 errPw.println(tag + "Expected 0 or 1 values for type " + type 1048 + ". Found: " + valueList.size()); 1049 return null; 1050 } 1051 break; 1052 } 1053 } 1054 1055 // Parse the value according to type and add it to the Bundle. 1056 switch (type) { 1057 case BOOLEAN: { 1058 if ("true".equalsIgnoreCase(valueList.get(0))) { 1059 bundle.putBoolean(key, true); 1060 } else if ("false".equalsIgnoreCase(valueList.get(0))) { 1061 bundle.putBoolean(key, false); 1062 } else { 1063 errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type); 1064 return null; 1065 } 1066 break; 1067 } 1068 case DOUBLE: { 1069 try { 1070 bundle.putDouble(key, Double.parseDouble(valueList.get(0))); 1071 } catch (NumberFormatException nfe) { 1072 // Not a valid double 1073 errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type); 1074 return null; 1075 } 1076 break; 1077 } 1078 case DOUBLE_ARRAY: { 1079 double[] valueDoubleArray = null; 1080 if (valueList.size() > 0) { 1081 valueDoubleArray = new double[valueList.size()]; 1082 for (int i = 0; i < valueList.size(); i++) { 1083 try { 1084 valueDoubleArray[i] = Double.parseDouble(valueList.get(i)); 1085 } catch (NumberFormatException nfe) { 1086 // Not a valid double 1087 errPw.println( 1088 tag + "Unable to parse " + valueList.get(i) + " as a double."); 1089 return null; 1090 } 1091 } 1092 } 1093 bundle.putDoubleArray(key, valueDoubleArray); 1094 break; 1095 } 1096 case INT: { 1097 try { 1098 bundle.putInt(key, Integer.parseInt(valueList.get(0))); 1099 } catch (NumberFormatException nfe) { 1100 // Not a valid integer 1101 errPw.println(tag + "Unable to parse " + valueList.get(0) + " as an " + type); 1102 return null; 1103 } 1104 break; 1105 } 1106 case INT_ARRAY: { 1107 int[] valueIntArray = null; 1108 if (valueList.size() > 0) { 1109 valueIntArray = new int[valueList.size()]; 1110 for (int i = 0; i < valueList.size(); i++) { 1111 try { 1112 valueIntArray[i] = Integer.parseInt(valueList.get(i)); 1113 } catch (NumberFormatException nfe) { 1114 // Not a valid integer 1115 errPw.println(tag 1116 + "Unable to parse " + valueList.get(i) + " as an integer."); 1117 return null; 1118 } 1119 } 1120 } 1121 bundle.putIntArray(key, valueIntArray); 1122 break; 1123 } 1124 case LONG: { 1125 try { 1126 bundle.putLong(key, Long.parseLong(valueList.get(0))); 1127 } catch (NumberFormatException nfe) { 1128 // Not a valid long 1129 errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type); 1130 return null; 1131 } 1132 break; 1133 } 1134 case LONG_ARRAY: { 1135 long[] valueLongArray = null; 1136 if (valueList.size() > 0) { 1137 valueLongArray = new long[valueList.size()]; 1138 for (int i = 0; i < valueList.size(); i++) { 1139 try { 1140 valueLongArray[i] = Long.parseLong(valueList.get(i)); 1141 } catch (NumberFormatException nfe) { 1142 // Not a valid long 1143 errPw.println( 1144 tag + "Unable to parse " + valueList.get(i) + " as a long"); 1145 return null; 1146 } 1147 } 1148 } 1149 bundle.putLongArray(key, valueLongArray); 1150 break; 1151 } 1152 case STRING: { 1153 String value = null; 1154 if (valueList.size() > 0) { 1155 value = valueList.get(0); 1156 } 1157 bundle.putString(key, value); 1158 break; 1159 } 1160 case STRING_ARRAY: { 1161 String[] valueStringArray = null; 1162 if (valueList.size() > 0) { 1163 valueStringArray = new String[valueList.size()]; 1164 valueList.toArray(valueStringArray); 1165 } 1166 bundle.putStringArray(key, valueStringArray); 1167 break; 1168 } 1169 } 1170 return bundle; 1171 } 1172 } 1173