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