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