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