blob: 672a27ffa2bed5312199b73b99b0a5433734d551 [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.PersistableBundle;
Hall Liud892bec2018-11-30 14:51:45 -080022import android.os.Process;
Brad Ebinger4dc095a2018-04-03 15:17:52 -070023import android.os.RemoteException;
24import android.os.ShellCommand;
Torbjorn Eklund1050cb02018-11-16 14:05:38 +010025import android.telephony.CarrierConfigManager;
26import android.telephony.SubscriptionInfo;
Brad Ebinger4dc095a2018-04-03 15:17:52 -070027import android.telephony.SubscriptionManager;
sqian9d4df8b2019-01-15 18:32:07 -080028import android.telephony.emergency.EmergencyNumber;
Brad Ebinger4dc095a2018-04-03 15:17:52 -070029import android.util.Log;
30
31import com.android.internal.telephony.ITelephony;
sqian9d4df8b2019-01-15 18:32:07 -080032import com.android.internal.telephony.emergency.EmergencyNumberTracker;
Meng Wangc4f61042019-11-21 10:51:05 -080033import com.android.internal.telephony.util.TelephonyUtils;
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
Torbjorn Eklund723527a2019-02-13 11:16:25 +010086 private class CcOptionParseResult {
87 public int mSubId;
88 public boolean mPersistent;
89 }
90
Torbjorn Eklund1050cb02018-11-16 14:05:38 +010091 // Maps carrier config keys to type. It is possible to infer the type for most carrier config
92 // keys by looking at the end of the string which usually tells the type.
93 // For instance: "xxxx_string", "xxxx_string_array", etc.
94 // The carrier config keys in this map does not follow this convention. It is therefore not
95 // possible to infer the type for these keys by looking at the string.
96 private static final Map<String, CcType> CC_TYPE_MAP = new HashMap<String, CcType>() {{
97 put(CarrierConfigManager.Gps.KEY_A_GLONASS_POS_PROTOCOL_SELECT_STRING, CcType.STRING);
98 put(CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, CcType.STRING);
99 put(CarrierConfigManager.Gps.KEY_GPS_LOCK_STRING, CcType.STRING);
100 put(CarrierConfigManager.Gps.KEY_LPP_PROFILE_STRING, CcType.STRING);
101 put(CarrierConfigManager.Gps.KEY_NFW_PROXY_APPS_STRING, CcType.STRING);
102 put(CarrierConfigManager.Gps.KEY_SUPL_ES_STRING, CcType.STRING);
103 put(CarrierConfigManager.Gps.KEY_SUPL_HOST_STRING, CcType.STRING);
104 put(CarrierConfigManager.Gps.KEY_SUPL_MODE_STRING, CcType.STRING);
105 put(CarrierConfigManager.Gps.KEY_SUPL_PORT_STRING, CcType.STRING);
106 put(CarrierConfigManager.Gps.KEY_SUPL_VER_STRING, CcType.STRING);
107 put(CarrierConfigManager.Gps.KEY_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL_STRING,
108 CcType.STRING);
109 put(CarrierConfigManager.KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
110 CcType.STRING_ARRAY);
111 put(CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
112 CcType.STRING_ARRAY);
113 put(CarrierConfigManager.KEY_CARRIER_CALL_SCREENING_APP_STRING, CcType.STRING);
114 put(CarrierConfigManager.KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING, CcType.STRING);
115 put(CarrierConfigManager.KEY_MMS_HTTP_PARAMS_STRING, CcType.STRING);
116 put(CarrierConfigManager.KEY_MMS_NAI_SUFFIX_STRING, CcType.STRING);
117 put(CarrierConfigManager.KEY_MMS_UA_PROF_TAG_NAME_STRING, CcType.STRING);
118 put(CarrierConfigManager.KEY_MMS_UA_PROF_URL_STRING, CcType.STRING);
119 put(CarrierConfigManager.KEY_MMS_USER_AGENT_STRING, CcType.STRING);
120 put(CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES, CcType.STRING_ARRAY);
121 }
122 };
123
124 public TelephonyShellCommand(ITelephony binder, Context context) {
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700125 mInterface = binder;
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100126 mCarrierConfigManager =
127 (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
128 mSubscriptionManager = (SubscriptionManager)
129 context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700130 }
131
132 @Override
133 public int onCommand(String cmd) {
134 if (cmd == null) {
135 return handleDefaultCommands(null);
136 }
137
138 switch (cmd) {
139 case IMS_SUBCOMMAND: {
140 return handleImsCommand();
141 }
Hall Liud892bec2018-11-30 14:51:45 -0800142 case NUMBER_VERIFICATION_SUBCOMMAND:
143 return handleNumberVerificationCommand();
sqian9d4df8b2019-01-15 18:32:07 -0800144 case EMERGENCY_NUMBER_TEST_MODE:
145 return handleEmergencyNumberTestModeCommand();
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100146 case CARRIER_CONFIG_SUBCOMMAND: {
147 return handleCcCommand();
148 }
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700149 default: {
150 return handleDefaultCommands(cmd);
151 }
152 }
153 }
154
155 @Override
156 public void onHelp() {
157 PrintWriter pw = getOutPrintWriter();
158 pw.println("Telephony Commands:");
159 pw.println(" help");
160 pw.println(" Print this help text.");
161 pw.println(" ims");
162 pw.println(" IMS Commands.");
sqian9d4df8b2019-01-15 18:32:07 -0800163 pw.println(" emergency-number-test-mode");
164 pw.println(" Emergency Number Test Mode Commands.");
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100165 pw.println(" cc");
166 pw.println(" Carrier Config Commands.");
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700167 onHelpIms();
sqian9d4df8b2019-01-15 18:32:07 -0800168 onHelpEmergencyNumber();
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100169 onHelpCc();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700170 }
171
172 private void onHelpIms() {
173 PrintWriter pw = getOutPrintWriter();
174 pw.println("IMS Commands:");
175 pw.println(" ims set-ims-service [-s SLOT_ID] (-c | -d) PACKAGE_NAME");
176 pw.println(" Sets the ImsService defined in PACKAGE_NAME to to be the bound");
177 pw.println(" ImsService. Options are:");
178 pw.println(" -s: the slot ID that the ImsService should be bound for. If no option");
179 pw.println(" is specified, it will choose the default voice SIM slot.");
180 pw.println(" -c: Override the ImsService defined in the carrier configuration.");
181 pw.println(" -d: Override the ImsService defined in the device overlay.");
182 pw.println(" ims get-ims-service [-s SLOT_ID] [-c | -d]");
183 pw.println(" Gets the package name of the currently defined ImsService.");
184 pw.println(" Options are:");
185 pw.println(" -s: The SIM slot ID for the registered ImsService. If no option");
186 pw.println(" is specified, it will choose the default voice SIM slot.");
187 pw.println(" -c: The ImsService defined as the carrier configured ImsService.");
188 pw.println(" -c: The ImsService defined as the device default ImsService.");
189 pw.println(" ims enable [-s SLOT_ID]");
190 pw.println(" enables IMS for the SIM slot specified, or for the default voice SIM slot");
191 pw.println(" if none is specified.");
192 pw.println(" ims disable [-s SLOT_ID]");
193 pw.println(" disables IMS for the SIM slot specified, or for the default voice SIM");
194 pw.println(" slot if none is specified.");
Tyler Gunn7bcdc742019-10-04 15:56:59 -0700195 pw.println(" ims conference-event-package [enable/disable]");
196 pw.println(" enables or disables handling or network conference event package data.");
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700197 }
198
Hall Liud892bec2018-11-30 14:51:45 -0800199 private void onHelpNumberVerification() {
200 PrintWriter pw = getOutPrintWriter();
201 pw.println("Number verification commands");
202 pw.println(" numverify override-package PACKAGE_NAME;");
203 pw.println(" Set the authorized package for number verification.");
204 pw.println(" Leave the package name blank to reset.");
Hall Liuca5af3a2018-12-04 16:58:23 -0800205 pw.println(" numverify fake-call NUMBER;");
206 pw.println(" Fake an incoming call from NUMBER. This is for testing. Output will be");
207 pw.println(" 1 if the call would have been intercepted, 0 otherwise.");
Hall Liud892bec2018-11-30 14:51:45 -0800208 }
209
sqian9d4df8b2019-01-15 18:32:07 -0800210 private void onHelpEmergencyNumber() {
211 PrintWriter pw = getOutPrintWriter();
212 pw.println("Emergency Number Test Mode Commands:");
213 pw.println(" emergency-number-test-mode ");
214 pw.println(" Add(-a), Clear(-c), Print (-p) or Remove(-r) the emergency number list in"
215 + " the test mode");
216 pw.println(" -a <emergency number address>: add an emergency number address for the"
sqian9121f982019-03-14 19:45:39 -0700217 + " test mode, only allows '0'-'9', '*', '#' or '+'.");
sqian9d4df8b2019-01-15 18:32:07 -0800218 pw.println(" -c: clear the emergency number list in the test mode.");
219 pw.println(" -r <emergency number address>: remove an existing emergency number"
sqian9121f982019-03-14 19:45:39 -0700220 + " address added by the test mode, only allows '0'-'9', '*', '#' or '+'.");
sqian9d4df8b2019-01-15 18:32:07 -0800221 pw.println(" -p: get the full emergency number list in the test mode.");
222 }
223
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100224 private void onHelpCc() {
225 PrintWriter pw = getOutPrintWriter();
226 pw.println("Carrier Config Commands:");
227 pw.println(" cc get-value [-s SLOT_ID] [KEY]");
228 pw.println(" Print carrier config values.");
229 pw.println(" Options are:");
230 pw.println(" -s: The SIM slot ID to read carrier config value for. If no option");
231 pw.println(" is specified, it will choose the default voice SIM slot.");
232 pw.println(" KEY: The key to the carrier config value to print. All values are printed");
233 pw.println(" if KEY is not specified.");
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100234 pw.println(" cc set-value [-s SLOT_ID] [-p] KEY [NEW_VALUE]");
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100235 pw.println(" Set carrier config KEY to NEW_VALUE.");
236 pw.println(" Options are:");
237 pw.println(" -s: The SIM slot ID to set carrier config value for. If no option");
238 pw.println(" is specified, it will choose the default voice SIM slot.");
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100239 pw.println(" -p: Value will be stored persistent");
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100240 pw.println(" NEW_VALUE specifies the new value for carrier config KEY. Null will be");
241 pw.println(" used if NEW_VALUE is not set. Strings should be encapsulated with");
242 pw.println(" quotation marks. Spaces needs to be escaped. Example: \"Hello\\ World\"");
243 pw.println(" Separate items in arrays with space . Example: \"item1\" \"item2\"");
244 pw.println(" cc clear-values [-s SLOT_ID]");
245 pw.println(" Clear all carrier override values that has previously been set");
246 pw.println(" with set-value");
247 pw.println(" Options are:");
248 pw.println(" -s: The SIM slot ID to clear carrier config values for. If no option");
249 pw.println(" is specified, it will choose the default voice SIM slot.");
250 }
251
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700252 private int handleImsCommand() {
253 String arg = getNextArg();
254 if (arg == null) {
255 onHelpIms();
256 return 0;
257 }
258
259 switch (arg) {
260 case IMS_SET_CARRIER_SERVICE: {
261 return handleImsSetServiceCommand();
262 }
263 case IMS_GET_CARRIER_SERVICE: {
264 return handleImsGetServiceCommand();
265 }
266 case IMS_ENABLE: {
267 return handleEnableIms();
268 }
269 case IMS_DISABLE: {
270 return handleDisableIms();
271 }
Tyler Gunn7bcdc742019-10-04 15:56:59 -0700272 case IMS_CEP: {
273 return handleCepChange();
274 }
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700275 }
276
277 return -1;
278 }
279
sqian9d4df8b2019-01-15 18:32:07 -0800280 private int handleEmergencyNumberTestModeCommand() {
281 PrintWriter errPw = getErrPrintWriter();
282 String opt = getNextOption();
283 if (opt == null) {
284 onHelpEmergencyNumber();
285 return 0;
286 }
287
288 switch (opt) {
289 case "-a": {
290 String emergencyNumberCmd = getNextArgRequired();
291 if (emergencyNumberCmd == null
292 || !EmergencyNumber.validateEmergencyNumberAddress(emergencyNumberCmd)) {
sqian9121f982019-03-14 19:45:39 -0700293 errPw.println("An emergency number (only allow '0'-'9', '*', '#' or '+') needs"
sqian9d4df8b2019-01-15 18:32:07 -0800294 + " to be specified after -a in the command ");
295 return -1;
296 }
297 try {
298 mInterface.updateEmergencyNumberListTestMode(
299 EmergencyNumberTracker.ADD_EMERGENCY_NUMBER_TEST_MODE,
300 new EmergencyNumber(emergencyNumberCmd, "", "",
301 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
302 new ArrayList<String>(),
303 EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST,
304 EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
305 } catch (RemoteException ex) {
306 Log.w(LOG_TAG, "emergency-number-test-mode -a " + emergencyNumberCmd
307 + ", error " + ex.getMessage());
308 errPw.println("Exception: " + ex.getMessage());
309 return -1;
310 }
311 break;
312 }
313 case "-c": {
314 try {
315 mInterface.updateEmergencyNumberListTestMode(
316 EmergencyNumberTracker.RESET_EMERGENCY_NUMBER_TEST_MODE, null);
317 } catch (RemoteException ex) {
318 Log.w(LOG_TAG, "emergency-number-test-mode -c " + "error " + ex.getMessage());
319 errPw.println("Exception: " + ex.getMessage());
320 return -1;
321 }
322 break;
323 }
324 case "-r": {
325 String emergencyNumberCmd = getNextArgRequired();
326 if (emergencyNumberCmd == null
327 || !EmergencyNumber.validateEmergencyNumberAddress(emergencyNumberCmd)) {
sqian9121f982019-03-14 19:45:39 -0700328 errPw.println("An emergency number (only allow '0'-'9', '*', '#' or '+') needs"
sqian9d4df8b2019-01-15 18:32:07 -0800329 + " to be specified after -r in the command ");
330 return -1;
331 }
332 try {
333 mInterface.updateEmergencyNumberListTestMode(
334 EmergencyNumberTracker.REMOVE_EMERGENCY_NUMBER_TEST_MODE,
335 new EmergencyNumber(emergencyNumberCmd, "", "",
336 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
337 new ArrayList<String>(),
338 EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST,
339 EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
340 } catch (RemoteException ex) {
341 Log.w(LOG_TAG, "emergency-number-test-mode -r " + emergencyNumberCmd
342 + ", error " + ex.getMessage());
343 errPw.println("Exception: " + ex.getMessage());
344 return -1;
345 }
346 break;
347 }
348 case "-p": {
349 try {
350 getOutPrintWriter().println(mInterface.getEmergencyNumberListTestMode());
351 } catch (RemoteException ex) {
352 Log.w(LOG_TAG, "emergency-number-test-mode -p " + "error " + ex.getMessage());
353 errPw.println("Exception: " + ex.getMessage());
354 return -1;
355 }
356 break;
357 }
358 default:
359 onHelpEmergencyNumber();
360 break;
361 }
362 return 0;
363 }
364
Hall Liud892bec2018-11-30 14:51:45 -0800365 private int handleNumberVerificationCommand() {
366 String arg = getNextArg();
367 if (arg == null) {
368 onHelpNumberVerification();
369 return 0;
370 }
371
Hall Liuca5af3a2018-12-04 16:58:23 -0800372 if (!checkShellUid()) {
373 return -1;
374 }
375
Hall Liud892bec2018-11-30 14:51:45 -0800376 switch (arg) {
377 case NUMBER_VERIFICATION_OVERRIDE_PACKAGE: {
Hall Liud892bec2018-11-30 14:51:45 -0800378 NumberVerificationManager.overrideAuthorizedPackage(getNextArg());
379 return 0;
380 }
Hall Liuca5af3a2018-12-04 16:58:23 -0800381 case NUMBER_VERIFICATION_FAKE_CALL: {
382 boolean val = NumberVerificationManager.getInstance()
383 .checkIncomingCall(getNextArg());
384 getOutPrintWriter().println(val ? "1" : "0");
385 return 0;
386 }
Hall Liud892bec2018-11-30 14:51:45 -0800387 }
388
389 return -1;
390 }
391
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700392 // ims set-ims-service
393 private int handleImsSetServiceCommand() {
394 PrintWriter errPw = getErrPrintWriter();
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700395 int slotId = getDefaultSlot();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700396 Boolean isCarrierService = null;
397
398 String opt;
399 while ((opt = getNextOption()) != null) {
400 switch (opt) {
401 case "-s": {
402 try {
403 slotId = Integer.parseInt(getNextArgRequired());
404 } catch (NumberFormatException e) {
405 errPw.println("ims set-ims-service requires an integer as a SLOT_ID.");
406 return -1;
407 }
408 break;
409 }
410 case "-c": {
411 isCarrierService = true;
412 break;
413 }
414 case "-d": {
415 isCarrierService = false;
416 break;
417 }
418 }
419 }
420 // Mandatory param, either -c or -d
421 if (isCarrierService == null) {
422 errPw.println("ims set-ims-service requires either \"-c\" or \"-d\" to be set.");
423 return -1;
424 }
425
426 String packageName = getNextArg();
427
428 try {
429 if (packageName == null) {
430 packageName = "";
431 }
432 boolean result = mInterface.setImsService(slotId, isCarrierService, packageName);
433 if (VDBG) {
434 Log.v(LOG_TAG, "ims set-ims-service -s " + slotId + " "
435 + (isCarrierService ? "-c " : "-d ") + packageName + ", result=" + result);
436 }
437 getOutPrintWriter().println(result);
438 } catch (RemoteException e) {
439 Log.w(LOG_TAG, "ims set-ims-service -s " + slotId + " "
440 + (isCarrierService ? "-c " : "-d ") + packageName + ", error"
441 + e.getMessage());
442 errPw.println("Exception: " + e.getMessage());
443 return -1;
444 }
445 return 0;
446 }
447
448 // ims get-ims-service
449 private int handleImsGetServiceCommand() {
450 PrintWriter errPw = getErrPrintWriter();
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700451 int slotId = getDefaultSlot();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700452 Boolean isCarrierService = null;
453
454 String opt;
455 while ((opt = getNextOption()) != null) {
456 switch (opt) {
457 case "-s": {
458 try {
459 slotId = Integer.parseInt(getNextArgRequired());
460 } catch (NumberFormatException e) {
461 errPw.println("ims set-ims-service requires an integer as a SLOT_ID.");
462 return -1;
463 }
464 break;
465 }
466 case "-c": {
467 isCarrierService = true;
468 break;
469 }
470 case "-d": {
471 isCarrierService = false;
472 break;
473 }
474 }
475 }
476 // Mandatory param, either -c or -d
477 if (isCarrierService == null) {
478 errPw.println("ims set-ims-service requires either \"-c\" or \"-d\" to be set.");
479 return -1;
480 }
481
482 String result;
483 try {
484 result = mInterface.getImsService(slotId, isCarrierService);
485 } catch (RemoteException e) {
486 return -1;
487 }
488 if (VDBG) {
489 Log.v(LOG_TAG, "ims get-ims-service -s " + slotId + " "
490 + (isCarrierService ? "-c " : "-d ") + ", returned: " + result);
491 }
492 getOutPrintWriter().println(result);
493 return 0;
494 }
495
496 private int handleEnableIms() {
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700497 int slotId = getDefaultSlot();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700498 String opt;
499 while ((opt = getNextOption()) != null) {
500 switch (opt) {
501 case "-s": {
502 try {
503 slotId = Integer.parseInt(getNextArgRequired());
504 } catch (NumberFormatException e) {
505 getErrPrintWriter().println("ims enable requires an integer as a SLOT_ID.");
506 return -1;
507 }
508 break;
509 }
510 }
511 }
512 try {
513 mInterface.enableIms(slotId);
514 } catch (RemoteException e) {
515 return -1;
516 }
517 if (VDBG) {
518 Log.v(LOG_TAG, "ims enable -s " + slotId);
519 }
520 return 0;
521 }
522
523 private int handleDisableIms() {
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700524 int slotId = getDefaultSlot();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700525 String opt;
526 while ((opt = getNextOption()) != null) {
527 switch (opt) {
528 case "-s": {
529 try {
530 slotId = Integer.parseInt(getNextArgRequired());
531 } catch (NumberFormatException e) {
532 getErrPrintWriter().println(
533 "ims disable requires an integer as a SLOT_ID.");
534 return -1;
535 }
536 break;
537 }
538 }
539 }
540 try {
541 mInterface.disableIms(slotId);
542 } catch (RemoteException e) {
543 return -1;
544 }
545 if (VDBG) {
546 Log.v(LOG_TAG, "ims disable -s " + slotId);
547 }
548 return 0;
549 }
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700550
Tyler Gunn7bcdc742019-10-04 15:56:59 -0700551 private int handleCepChange() {
552 Log.i(LOG_TAG, "handleCepChange");
553 String opt = getNextArg();
554 if (opt == null) {
555 return -1;
556 }
557 boolean isCepEnabled = opt.equals("enable");
558
559 try {
560 mInterface.setCepEnabled(isCepEnabled);
561 } catch (RemoteException e) {
562 return -1;
563 }
564 return 0;
565 }
566
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700567 private int getDefaultSlot() {
568 int slotId = SubscriptionManager.getDefaultVoicePhoneId();
569 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX
570 || slotId == SubscriptionManager.DEFAULT_PHONE_INDEX) {
571 // If there is no default, default to slot 0.
572 slotId = DEFAULT_PHONE_ID;
573 }
574 return slotId;
575 }
sqian2fff4a32018-11-05 14:18:37 -0800576
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100577 // Parse options related to Carrier Config Commands.
578 private CcOptionParseResult parseCcOptions(String tag, boolean allowOptionPersistent) {
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100579 PrintWriter errPw = getErrPrintWriter();
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100580 CcOptionParseResult result = new CcOptionParseResult();
581 result.mSubId = SubscriptionManager.getDefaultSubscriptionId();
582 result.mPersistent = false;
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100583
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100584 String opt;
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100585 while ((opt = getNextOption()) != null) {
586 switch (opt) {
587 case "-s": {
588 try {
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100589 result.mSubId = slotStringToSubId(tag, getNextArgRequired());
590 if (!SubscriptionManager.isValidSubscriptionId(result.mSubId)) {
591 errPw.println(tag + "No valid subscription found.");
592 return null;
593 }
594
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100595 } catch (IllegalArgumentException e) {
596 // Missing slot id
597 errPw.println(tag + "SLOT_ID expected after -s.");
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100598 return null;
599 }
600 break;
601 }
602 case "-p": {
603 if (allowOptionPersistent) {
604 result.mPersistent = true;
605 } else {
606 errPw.println(tag + "Unexpected option " + opt);
607 return null;
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100608 }
609 break;
610 }
611 default: {
612 errPw.println(tag + "Unknown option " + opt);
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100613 return null;
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100614 }
615 }
616 }
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100617 return result;
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100618 }
619
620 private int slotStringToSubId(String tag, String slotString) {
621 int slotId = -1;
622 try {
623 slotId = Integer.parseInt(slotString);
624 } catch (NumberFormatException e) {
625 getErrPrintWriter().println(tag + slotString + " is not a valid SLOT_ID.");
626 return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
627 }
628
629 SubscriptionInfo subInfo =
630 mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(slotId);
631 if (subInfo == null) {
632 getErrPrintWriter().println(tag + "No subscription found in slot " + slotId + ".");
633 return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
634 }
635 return subInfo.getSubscriptionId();
636 }
637
Hall Liud892bec2018-11-30 14:51:45 -0800638 private boolean checkShellUid() {
Hall Liu2ddfc7e2018-12-06 13:09:45 -0800639 // adb can run as root or as shell, depending on whether the device is rooted.
640 return Binder.getCallingUid() == Process.SHELL_UID
641 || Binder.getCallingUid() == Process.ROOT_UID;
Hall Liud892bec2018-11-30 14:51:45 -0800642 }
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100643
644 private int handleCcCommand() {
645 // Verify that the user is allowed to run the command. Only allowed in rooted device in a
646 // non user build.
Meng Wangc4f61042019-11-21 10:51:05 -0800647 if (Binder.getCallingUid() != Process.ROOT_UID || TelephonyUtils.IS_USER) {
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100648 getErrPrintWriter().println("cc: Permission denied.");
649 return -1;
650 }
651
652 String arg = getNextArg();
653 if (arg == null) {
654 onHelpCc();
655 return 0;
656 }
657
658 switch (arg) {
659 case CC_GET_VALUE: {
660 return handleCcGetValue();
661 }
662 case CC_SET_VALUE: {
663 return handleCcSetValue();
664 }
665 case CC_CLEAR_VALUES: {
666 return handleCcClearValues();
667 }
668 default: {
669 getErrPrintWriter().println("cc: Unknown argument: " + arg);
670 }
671 }
672 return -1;
673 }
674
675 // cc get-value
676 private int handleCcGetValue() {
677 PrintWriter errPw = getErrPrintWriter();
678 String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_GET_VALUE + ": ";
679 String key = null;
680
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100681 // Parse all options
682 CcOptionParseResult options = parseCcOptions(tag, false);
683 if (options == null) {
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100684 return -1;
685 }
686
687 // Get bundle containing all carrier configuration values.
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100688 PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(options.mSubId);
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100689 if (bundle == null) {
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100690 errPw.println(tag + "No carrier config values found for subId " + options.mSubId + ".");
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100691 return -1;
692 }
693
694 // Get the key.
695 key = getNextArg();
696 if (key != null) {
697 // A key was provided. Verify if it is a valid key
698 if (!bundle.containsKey(key)) {
699 errPw.println(tag + key + " is not a valid key.");
700 return -1;
701 }
702
703 // Print the carrier config value for key.
704 getOutPrintWriter().println(ccValueToString(key, getType(tag, key, bundle), bundle));
705 } else {
706 // No key provided. Show all values.
707 // Iterate over a sorted list of all carrier config keys and print them.
708 TreeSet<String> sortedSet = new TreeSet<String>(bundle.keySet());
709 for (String k : sortedSet) {
710 getOutPrintWriter().println(ccValueToString(k, getType(tag, k, bundle), bundle));
711 }
712 }
713 return 0;
714 }
715
716 // cc set-value
717 private int handleCcSetValue() {
718 PrintWriter errPw = getErrPrintWriter();
719 String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_SET_VALUE + ": ";
720
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100721 // Parse all options
722 CcOptionParseResult options = parseCcOptions(tag, true);
723 if (options == null) {
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100724 return -1;
725 }
726
727 // Get bundle containing all current carrier configuration values.
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100728 PersistableBundle originalValues = mCarrierConfigManager.getConfigForSubId(options.mSubId);
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100729 if (originalValues == null) {
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100730 errPw.println(tag + "No carrier config values found for subId " + options.mSubId + ".");
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100731 return -1;
732 }
733
734 // Get the key.
735 String key = getNextArg();
736 if (key == null || key.equals("")) {
737 errPw.println(tag + "KEY is missing");
738 return -1;
739 }
740
741 // Verify if the key is valid
742 if (!originalValues.containsKey(key)) {
743 errPw.println(tag + key + " is not a valid key.");
744 return -1;
745 }
746
747 // Remaining arguments is a list of new values. Add them all into an ArrayList.
748 ArrayList<String> valueList = new ArrayList<String>();
749 while (peekNextArg() != null) {
750 valueList.add(getNextArg());
751 }
752
753 // Find the type of the carrier config value
754 CcType type = getType(tag, key, originalValues);
755 if (type == CcType.UNKNOWN) {
756 errPw.println(tag + "ERROR: Not possible to override key with unknown type.");
757 return -1;
758 }
759
760 // Create an override bundle containing the key and value that should be overriden.
761 PersistableBundle overrideBundle = getOverrideBundle(tag, type, key, valueList);
762 if (overrideBundle == null) {
763 return -1;
764 }
765
766 // Override the value
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100767 mCarrierConfigManager.overrideConfig(options.mSubId, overrideBundle, options.mPersistent);
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100768
769 // Find bundle containing all new carrier configuration values after the override.
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100770 PersistableBundle newValues = mCarrierConfigManager.getConfigForSubId(options.mSubId);
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100771 if (newValues == null) {
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100772 errPw.println(tag + "No carrier config values found for subId " + options.mSubId + ".");
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100773 return -1;
774 }
775
776 // Print the original and new value.
777 String originalValueString = ccValueToString(key, type, originalValues);
778 String newValueString = ccValueToString(key, type, newValues);
779 getOutPrintWriter().println("Previous value: \n" + originalValueString);
780 getOutPrintWriter().println("New value: \n" + newValueString);
781
782 return 0;
783 }
784
785 // cc clear-values
786 private int handleCcClearValues() {
787 PrintWriter errPw = getErrPrintWriter();
788 String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_CLEAR_VALUES + ": ";
789
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100790 // Parse all options
791 CcOptionParseResult options = parseCcOptions(tag, false);
792 if (options == null) {
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100793 return -1;
794 }
795
796 // Clear all values that has previously been set.
Torbjorn Eklund723527a2019-02-13 11:16:25 +0100797 mCarrierConfigManager.overrideConfig(options.mSubId, null, true);
Torbjorn Eklund1050cb02018-11-16 14:05:38 +0100798 getOutPrintWriter()
799 .println("All previously set carrier config override values has been cleared");
800 return 0;
801 }
802
803 private CcType getType(String tag, String key, PersistableBundle bundle) {
804 // Find the type by checking the type of the current value stored in the bundle.
805 Object value = bundle.get(key);
806
807 if (CC_TYPE_MAP.containsKey(key)) {
808 return CC_TYPE_MAP.get(key);
809 } else if (value != null) {
810 if (value instanceof Boolean) {
811 return CcType.BOOLEAN;
812 } else if (value instanceof Double) {
813 return CcType.DOUBLE;
814 } else if (value instanceof double[]) {
815 return CcType.DOUBLE_ARRAY;
816 } else if (value instanceof Integer) {
817 return CcType.INT;
818 } else if (value instanceof int[]) {
819 return CcType.INT_ARRAY;
820 } else if (value instanceof Long) {
821 return CcType.LONG;
822 } else if (value instanceof long[]) {
823 return CcType.LONG_ARRAY;
824 } else if (value instanceof String) {
825 return CcType.STRING;
826 } else if (value instanceof String[]) {
827 return CcType.STRING_ARRAY;
828 }
829 } else {
830 // Current value was null and can therefore not be used in order to find the type.
831 // Check the name of the key to infer the type. This check is not needed for primitive
832 // data types (boolean, double, int and long), since they can not be null.
833 if (key.endsWith("double_array")) {
834 return CcType.DOUBLE_ARRAY;
835 }
836 if (key.endsWith("int_array")) {
837 return CcType.INT_ARRAY;
838 }
839 if (key.endsWith("long_array")) {
840 return CcType.LONG_ARRAY;
841 }
842 if (key.endsWith("string")) {
843 return CcType.STRING;
844 }
845 if (key.endsWith("string_array") || key.endsWith("strings")) {
846 return CcType.STRING_ARRAY;
847 }
848 }
849
850 // Not possible to infer the type by looking at the current value or the key.
851 PrintWriter errPw = getErrPrintWriter();
852 errPw.println(tag + "ERROR: " + key + " has unknown type.");
853 return CcType.UNKNOWN;
854 }
855
856 private String ccValueToString(String key, CcType type, PersistableBundle bundle) {
857 String result;
858 StringBuilder valueString = new StringBuilder();
859 String typeString = type.toString();
860 Object value = bundle.get(key);
861
862 if (value == null) {
863 valueString.append("null");
864 } else {
865 switch (type) {
866 case DOUBLE_ARRAY: {
867 // Format the string representation of the int array as value1 value2......
868 double[] valueArray = (double[]) value;
869 for (int i = 0; i < valueArray.length; i++) {
870 if (i != 0) {
871 valueString.append(" ");
872 }
873 valueString.append(valueArray[i]);
874 }
875 break;
876 }
877 case INT_ARRAY: {
878 // Format the string representation of the int array as value1 value2......
879 int[] valueArray = (int[]) value;
880 for (int i = 0; i < valueArray.length; i++) {
881 if (i != 0) {
882 valueString.append(" ");
883 }
884 valueString.append(valueArray[i]);
885 }
886 break;
887 }
888 case LONG_ARRAY: {
889 // Format the string representation of the int array as value1 value2......
890 long[] valueArray = (long[]) value;
891 for (int i = 0; i < valueArray.length; i++) {
892 if (i != 0) {
893 valueString.append(" ");
894 }
895 valueString.append(valueArray[i]);
896 }
897 break;
898 }
899 case STRING: {
900 valueString.append("\"" + value.toString() + "\"");
901 break;
902 }
903 case STRING_ARRAY: {
904 // Format the string representation of the string array as "value1" "value2"....
905 String[] valueArray = (String[]) value;
906 for (int i = 0; i < valueArray.length; i++) {
907 if (i != 0) {
908 valueString.append(" ");
909 }
910 if (valueArray[i] != null) {
911 valueString.append("\"" + valueArray[i] + "\"");
912 } else {
913 valueString.append("null");
914 }
915 }
916 break;
917 }
918 default: {
919 valueString.append(value.toString());
920 }
921 }
922 }
923 return String.format("%-70s %-15s %s", key, typeString, valueString);
924 }
925
926 private PersistableBundle getOverrideBundle(String tag, CcType type, String key,
927 ArrayList<String> valueList) {
928 PrintWriter errPw = getErrPrintWriter();
929 PersistableBundle bundle = new PersistableBundle();
930
931 // First verify that a valid number of values has been provided for the type.
932 switch (type) {
933 case BOOLEAN:
934 case DOUBLE:
935 case INT:
936 case LONG: {
937 if (valueList.size() != 1) {
938 errPw.println(tag + "Expected 1 value for type " + type
939 + ". Found: " + valueList.size());
940 return null;
941 }
942 break;
943 }
944 case STRING: {
945 if (valueList.size() > 1) {
946 errPw.println(tag + "Expected 0 or 1 values for type " + type
947 + ". Found: " + valueList.size());
948 return null;
949 }
950 break;
951 }
952 }
953
954 // Parse the value according to type and add it to the Bundle.
955 switch (type) {
956 case BOOLEAN: {
957 if ("true".equalsIgnoreCase(valueList.get(0))) {
958 bundle.putBoolean(key, true);
959 } else if ("false".equalsIgnoreCase(valueList.get(0))) {
960 bundle.putBoolean(key, false);
961 } else {
962 errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type);
963 return null;
964 }
965 break;
966 }
967 case DOUBLE: {
968 try {
969 bundle.putDouble(key, Double.parseDouble(valueList.get(0)));
970 } catch (NumberFormatException nfe) {
971 // Not a valid double
972 errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type);
973 return null;
974 }
975 break;
976 }
977 case DOUBLE_ARRAY: {
978 double[] valueDoubleArray = null;
979 if (valueList.size() > 0) {
980 valueDoubleArray = new double[valueList.size()];
981 for (int i = 0; i < valueList.size(); i++) {
982 try {
983 valueDoubleArray[i] = Double.parseDouble(valueList.get(i));
984 } catch (NumberFormatException nfe) {
985 // Not a valid double
986 errPw.println(
987 tag + "Unable to parse " + valueList.get(i) + " as a double.");
988 return null;
989 }
990 }
991 }
992 bundle.putDoubleArray(key, valueDoubleArray);
993 break;
994 }
995 case INT: {
996 try {
997 bundle.putInt(key, Integer.parseInt(valueList.get(0)));
998 } catch (NumberFormatException nfe) {
999 // Not a valid integer
1000 errPw.println(tag + "Unable to parse " + valueList.get(0) + " as an " + type);
1001 return null;
1002 }
1003 break;
1004 }
1005 case INT_ARRAY: {
1006 int[] valueIntArray = null;
1007 if (valueList.size() > 0) {
1008 valueIntArray = new int[valueList.size()];
1009 for (int i = 0; i < valueList.size(); i++) {
1010 try {
1011 valueIntArray[i] = Integer.parseInt(valueList.get(i));
1012 } catch (NumberFormatException nfe) {
1013 // Not a valid integer
1014 errPw.println(tag
1015 + "Unable to parse " + valueList.get(i) + " as an integer.");
1016 return null;
1017 }
1018 }
1019 }
1020 bundle.putIntArray(key, valueIntArray);
1021 break;
1022 }
1023 case LONG: {
1024 try {
1025 bundle.putLong(key, Long.parseLong(valueList.get(0)));
1026 } catch (NumberFormatException nfe) {
1027 // Not a valid long
1028 errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type);
1029 return null;
1030 }
1031 break;
1032 }
1033 case LONG_ARRAY: {
1034 long[] valueLongArray = null;
1035 if (valueList.size() > 0) {
1036 valueLongArray = new long[valueList.size()];
1037 for (int i = 0; i < valueList.size(); i++) {
1038 try {
1039 valueLongArray[i] = Long.parseLong(valueList.get(i));
1040 } catch (NumberFormatException nfe) {
1041 // Not a valid long
1042 errPw.println(
1043 tag + "Unable to parse " + valueList.get(i) + " as a long");
1044 return null;
1045 }
1046 }
1047 }
1048 bundle.putLongArray(key, valueLongArray);
1049 break;
1050 }
1051 case STRING: {
1052 String value = null;
1053 if (valueList.size() > 0) {
1054 value = valueList.get(0);
1055 }
1056 bundle.putString(key, value);
1057 break;
1058 }
1059 case STRING_ARRAY: {
1060 String[] valueStringArray = null;
1061 if (valueList.size() > 0) {
1062 valueStringArray = new String[valueList.size()];
1063 valueList.toArray(valueStringArray);
1064 }
1065 bundle.putStringArray(key, valueStringArray);
1066 break;
1067 }
1068 }
1069 return bundle;
1070 }
Brad Ebinger4dc095a2018-04-03 15:17:52 -07001071}