blob: a59c9c47f3e559990ef2578c77cb6faa556e2f1e [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
Hall Liud892bec2018-11-30 14:51:45 -080019import android.os.Binder;
20import android.os.Process;
Brad Ebinger4dc095a2018-04-03 15:17:52 -070021import android.os.RemoteException;
22import android.os.ShellCommand;
sqian2fff4a32018-11-05 14:18:37 -080023import android.os.UserHandle;
Brad Ebinger4dc095a2018-04-03 15:17:52 -070024import android.telephony.SubscriptionManager;
sqian9d4df8b2019-01-15 18:32:07 -080025import android.telephony.emergency.EmergencyNumber;
Brad Ebinger4dc095a2018-04-03 15:17:52 -070026import android.util.Log;
27
28import com.android.internal.telephony.ITelephony;
sqian9d4df8b2019-01-15 18:32:07 -080029import com.android.internal.telephony.emergency.EmergencyNumberTracker;
Brad Ebinger4dc095a2018-04-03 15:17:52 -070030
31import java.io.PrintWriter;
sqian9d4df8b2019-01-15 18:32:07 -080032import java.util.ArrayList;
Brad Ebinger4dc095a2018-04-03 15:17:52 -070033
34/**
35 * Takes actions based on the adb commands given by "adb shell cmd phone ...". Be careful, no
36 * permission checks have been done before onCommand was called. Make sure any commands processed
37 * here also contain the appropriate permissions checks.
38 */
39
40public class TelephonyShellCommand extends ShellCommand {
41
42 private static final String LOG_TAG = "TelephonyShellCommand";
43 // Don't commit with this true.
44 private static final boolean VDBG = true;
Brad Ebinger0aa2f242018-04-12 09:49:23 -070045 private static final int DEFAULT_PHONE_ID = 0;
Brad Ebinger4dc095a2018-04-03 15:17:52 -070046
47 private static final String IMS_SUBCOMMAND = "ims";
sqian2fff4a32018-11-05 14:18:37 -080048 private static final String SMS_SUBCOMMAND = "sms";
Hall Liud892bec2018-11-30 14:51:45 -080049 private static final String NUMBER_VERIFICATION_SUBCOMMAND = "numverify";
sqian9d4df8b2019-01-15 18:32:07 -080050 private static final String EMERGENCY_NUMBER_TEST_MODE = "emergency-number-test-mode";
Hall Liud892bec2018-11-30 14:51:45 -080051
Brad Ebinger4dc095a2018-04-03 15:17:52 -070052 private static final String IMS_SET_CARRIER_SERVICE = "set-ims-service";
53 private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service";
54 private static final String IMS_ENABLE = "enable";
55 private static final String IMS_DISABLE = "disable";
56
sqian2fff4a32018-11-05 14:18:37 -080057 private static final String SMS_GET_APPS = "get-apps";
58 private static final String SMS_GET_DEFAULT_APP = "get-default-app";
59 private static final String SMS_SET_DEFAULT_APP = "set-default-app";
60
Hall Liud892bec2018-11-30 14:51:45 -080061 private static final String NUMBER_VERIFICATION_OVERRIDE_PACKAGE = "override-package";
Hall Liuca5af3a2018-12-04 16:58:23 -080062 private static final String NUMBER_VERIFICATION_FAKE_CALL = "fake-call";
Hall Liud892bec2018-11-30 14:51:45 -080063
Brad Ebinger4dc095a2018-04-03 15:17:52 -070064 // Take advantage of existing methods that already contain permissions checks when possible.
65 private final ITelephony mInterface;
66
67 public TelephonyShellCommand(ITelephony binder) {
68 mInterface = binder;
69 }
70
71 @Override
72 public int onCommand(String cmd) {
73 if (cmd == null) {
74 return handleDefaultCommands(null);
75 }
76
77 switch (cmd) {
78 case IMS_SUBCOMMAND: {
79 return handleImsCommand();
80 }
sqian2fff4a32018-11-05 14:18:37 -080081 case SMS_SUBCOMMAND: {
82 return handleSmsCommand();
83 }
Hall Liud892bec2018-11-30 14:51:45 -080084 case NUMBER_VERIFICATION_SUBCOMMAND:
85 return handleNumberVerificationCommand();
sqian9d4df8b2019-01-15 18:32:07 -080086 case EMERGENCY_NUMBER_TEST_MODE:
87 return handleEmergencyNumberTestModeCommand();
Brad Ebinger4dc095a2018-04-03 15:17:52 -070088 default: {
89 return handleDefaultCommands(cmd);
90 }
91 }
92 }
93
94 @Override
95 public void onHelp() {
96 PrintWriter pw = getOutPrintWriter();
97 pw.println("Telephony Commands:");
98 pw.println(" help");
99 pw.println(" Print this help text.");
100 pw.println(" ims");
101 pw.println(" IMS Commands.");
sqian2fff4a32018-11-05 14:18:37 -0800102 pw.println(" sms");
103 pw.println(" SMS Commands.");
sqian9d4df8b2019-01-15 18:32:07 -0800104 pw.println(" emergency-number-test-mode");
105 pw.println(" Emergency Number Test Mode Commands.");
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700106 onHelpIms();
sqian2fff4a32018-11-05 14:18:37 -0800107 onHelpSms();
sqian9d4df8b2019-01-15 18:32:07 -0800108 onHelpEmergencyNumber();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700109 }
110
111 private void onHelpIms() {
112 PrintWriter pw = getOutPrintWriter();
113 pw.println("IMS Commands:");
114 pw.println(" ims set-ims-service [-s SLOT_ID] (-c | -d) PACKAGE_NAME");
115 pw.println(" Sets the ImsService defined in PACKAGE_NAME to to be the bound");
116 pw.println(" ImsService. Options are:");
117 pw.println(" -s: the slot ID that the ImsService should be bound for. If no option");
118 pw.println(" is specified, it will choose the default voice SIM slot.");
119 pw.println(" -c: Override the ImsService defined in the carrier configuration.");
120 pw.println(" -d: Override the ImsService defined in the device overlay.");
121 pw.println(" ims get-ims-service [-s SLOT_ID] [-c | -d]");
122 pw.println(" Gets the package name of the currently defined ImsService.");
123 pw.println(" Options are:");
124 pw.println(" -s: The SIM slot ID for the registered ImsService. If no option");
125 pw.println(" is specified, it will choose the default voice SIM slot.");
126 pw.println(" -c: The ImsService defined as the carrier configured ImsService.");
127 pw.println(" -c: The ImsService defined as the device default ImsService.");
128 pw.println(" ims enable [-s SLOT_ID]");
129 pw.println(" enables IMS for the SIM slot specified, or for the default voice SIM slot");
130 pw.println(" if none is specified.");
131 pw.println(" ims disable [-s SLOT_ID]");
132 pw.println(" disables IMS for the SIM slot specified, or for the default voice SIM");
133 pw.println(" slot if none is specified.");
134 }
135
sqian2fff4a32018-11-05 14:18:37 -0800136 private void onHelpSms() {
137 PrintWriter pw = getOutPrintWriter();
138 pw.println("SMS Commands:");
139 pw.println(" sms get-apps [--user USER_ID]");
140 pw.println(" Print all SMS apps on a user.");
141 pw.println(" sms get-default-app [--user USER_ID]");
142 pw.println(" Get the default SMS app.");
143 pw.println(" sms set-default-app [--user USER_ID] PACKAGE_NAME");
144 pw.println(" Set PACKAGE_NAME as the default SMS app.");
145 }
146
Hall Liud892bec2018-11-30 14:51:45 -0800147
148 private void onHelpNumberVerification() {
149 PrintWriter pw = getOutPrintWriter();
150 pw.println("Number verification commands");
151 pw.println(" numverify override-package PACKAGE_NAME;");
152 pw.println(" Set the authorized package for number verification.");
153 pw.println(" Leave the package name blank to reset.");
Hall Liuca5af3a2018-12-04 16:58:23 -0800154 pw.println(" numverify fake-call NUMBER;");
155 pw.println(" Fake an incoming call from NUMBER. This is for testing. Output will be");
156 pw.println(" 1 if the call would have been intercepted, 0 otherwise.");
Hall Liud892bec2018-11-30 14:51:45 -0800157 }
158
sqian9d4df8b2019-01-15 18:32:07 -0800159 private void onHelpEmergencyNumber() {
160 PrintWriter pw = getOutPrintWriter();
161 pw.println("Emergency Number Test Mode Commands:");
162 pw.println(" emergency-number-test-mode ");
163 pw.println(" Add(-a), Clear(-c), Print (-p) or Remove(-r) the emergency number list in"
164 + " the test mode");
165 pw.println(" -a <emergency number address>: add an emergency number address for the"
sqian9121f982019-03-14 19:45:39 -0700166 + " test mode, only allows '0'-'9', '*', '#' or '+'.");
sqian9d4df8b2019-01-15 18:32:07 -0800167 pw.println(" -c: clear the emergency number list in the test mode.");
168 pw.println(" -r <emergency number address>: remove an existing emergency number"
sqian9121f982019-03-14 19:45:39 -0700169 + " address added by the test mode, only allows '0'-'9', '*', '#' or '+'.");
sqian9d4df8b2019-01-15 18:32:07 -0800170 pw.println(" -p: get the full emergency number list in the test mode.");
171 }
172
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700173 private int handleImsCommand() {
174 String arg = getNextArg();
175 if (arg == null) {
176 onHelpIms();
177 return 0;
178 }
179
180 switch (arg) {
181 case IMS_SET_CARRIER_SERVICE: {
182 return handleImsSetServiceCommand();
183 }
184 case IMS_GET_CARRIER_SERVICE: {
185 return handleImsGetServiceCommand();
186 }
187 case IMS_ENABLE: {
188 return handleEnableIms();
189 }
190 case IMS_DISABLE: {
191 return handleDisableIms();
192 }
193 }
194
195 return -1;
196 }
197
sqian9d4df8b2019-01-15 18:32:07 -0800198 private int handleEmergencyNumberTestModeCommand() {
199 PrintWriter errPw = getErrPrintWriter();
200 String opt = getNextOption();
201 if (opt == null) {
202 onHelpEmergencyNumber();
203 return 0;
204 }
205
206 switch (opt) {
207 case "-a": {
208 String emergencyNumberCmd = getNextArgRequired();
209 if (emergencyNumberCmd == null
210 || !EmergencyNumber.validateEmergencyNumberAddress(emergencyNumberCmd)) {
sqian9121f982019-03-14 19:45:39 -0700211 errPw.println("An emergency number (only allow '0'-'9', '*', '#' or '+') needs"
sqian9d4df8b2019-01-15 18:32:07 -0800212 + " to be specified after -a in the command ");
213 return -1;
214 }
215 try {
216 mInterface.updateEmergencyNumberListTestMode(
217 EmergencyNumberTracker.ADD_EMERGENCY_NUMBER_TEST_MODE,
218 new EmergencyNumber(emergencyNumberCmd, "", "",
219 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
220 new ArrayList<String>(),
221 EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST,
222 EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
223 } catch (RemoteException ex) {
224 Log.w(LOG_TAG, "emergency-number-test-mode -a " + emergencyNumberCmd
225 + ", error " + ex.getMessage());
226 errPw.println("Exception: " + ex.getMessage());
227 return -1;
228 }
229 break;
230 }
231 case "-c": {
232 try {
233 mInterface.updateEmergencyNumberListTestMode(
234 EmergencyNumberTracker.RESET_EMERGENCY_NUMBER_TEST_MODE, null);
235 } catch (RemoteException ex) {
236 Log.w(LOG_TAG, "emergency-number-test-mode -c " + "error " + ex.getMessage());
237 errPw.println("Exception: " + ex.getMessage());
238 return -1;
239 }
240 break;
241 }
242 case "-r": {
243 String emergencyNumberCmd = getNextArgRequired();
244 if (emergencyNumberCmd == null
245 || !EmergencyNumber.validateEmergencyNumberAddress(emergencyNumberCmd)) {
sqian9121f982019-03-14 19:45:39 -0700246 errPw.println("An emergency number (only allow '0'-'9', '*', '#' or '+') needs"
sqian9d4df8b2019-01-15 18:32:07 -0800247 + " to be specified after -r in the command ");
248 return -1;
249 }
250 try {
251 mInterface.updateEmergencyNumberListTestMode(
252 EmergencyNumberTracker.REMOVE_EMERGENCY_NUMBER_TEST_MODE,
253 new EmergencyNumber(emergencyNumberCmd, "", "",
254 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
255 new ArrayList<String>(),
256 EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST,
257 EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
258 } catch (RemoteException ex) {
259 Log.w(LOG_TAG, "emergency-number-test-mode -r " + emergencyNumberCmd
260 + ", error " + ex.getMessage());
261 errPw.println("Exception: " + ex.getMessage());
262 return -1;
263 }
264 break;
265 }
266 case "-p": {
267 try {
268 getOutPrintWriter().println(mInterface.getEmergencyNumberListTestMode());
269 } catch (RemoteException ex) {
270 Log.w(LOG_TAG, "emergency-number-test-mode -p " + "error " + ex.getMessage());
271 errPw.println("Exception: " + ex.getMessage());
272 return -1;
273 }
274 break;
275 }
276 default:
277 onHelpEmergencyNumber();
278 break;
279 }
280 return 0;
281 }
282
Hall Liud892bec2018-11-30 14:51:45 -0800283 private int handleNumberVerificationCommand() {
284 String arg = getNextArg();
285 if (arg == null) {
286 onHelpNumberVerification();
287 return 0;
288 }
289
Hall Liuca5af3a2018-12-04 16:58:23 -0800290 if (!checkShellUid()) {
291 return -1;
292 }
293
Hall Liud892bec2018-11-30 14:51:45 -0800294 switch (arg) {
295 case NUMBER_VERIFICATION_OVERRIDE_PACKAGE: {
Hall Liud892bec2018-11-30 14:51:45 -0800296 NumberVerificationManager.overrideAuthorizedPackage(getNextArg());
297 return 0;
298 }
Hall Liuca5af3a2018-12-04 16:58:23 -0800299 case NUMBER_VERIFICATION_FAKE_CALL: {
300 boolean val = NumberVerificationManager.getInstance()
301 .checkIncomingCall(getNextArg());
302 getOutPrintWriter().println(val ? "1" : "0");
303 return 0;
304 }
Hall Liud892bec2018-11-30 14:51:45 -0800305 }
306
307 return -1;
308 }
309
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700310 // ims set-ims-service
311 private int handleImsSetServiceCommand() {
312 PrintWriter errPw = getErrPrintWriter();
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700313 int slotId = getDefaultSlot();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700314 Boolean isCarrierService = null;
315
316 String opt;
317 while ((opt = getNextOption()) != null) {
318 switch (opt) {
319 case "-s": {
320 try {
321 slotId = Integer.parseInt(getNextArgRequired());
322 } catch (NumberFormatException e) {
323 errPw.println("ims set-ims-service requires an integer as a SLOT_ID.");
324 return -1;
325 }
326 break;
327 }
328 case "-c": {
329 isCarrierService = true;
330 break;
331 }
332 case "-d": {
333 isCarrierService = false;
334 break;
335 }
336 }
337 }
338 // Mandatory param, either -c or -d
339 if (isCarrierService == null) {
340 errPw.println("ims set-ims-service requires either \"-c\" or \"-d\" to be set.");
341 return -1;
342 }
343
344 String packageName = getNextArg();
345
346 try {
347 if (packageName == null) {
348 packageName = "";
349 }
350 boolean result = mInterface.setImsService(slotId, isCarrierService, packageName);
351 if (VDBG) {
352 Log.v(LOG_TAG, "ims set-ims-service -s " + slotId + " "
353 + (isCarrierService ? "-c " : "-d ") + packageName + ", result=" + result);
354 }
355 getOutPrintWriter().println(result);
356 } catch (RemoteException e) {
357 Log.w(LOG_TAG, "ims set-ims-service -s " + slotId + " "
358 + (isCarrierService ? "-c " : "-d ") + packageName + ", error"
359 + e.getMessage());
360 errPw.println("Exception: " + e.getMessage());
361 return -1;
362 }
363 return 0;
364 }
365
366 // ims get-ims-service
367 private int handleImsGetServiceCommand() {
368 PrintWriter errPw = getErrPrintWriter();
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700369 int slotId = getDefaultSlot();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700370 Boolean isCarrierService = null;
371
372 String opt;
373 while ((opt = getNextOption()) != null) {
374 switch (opt) {
375 case "-s": {
376 try {
377 slotId = Integer.parseInt(getNextArgRequired());
378 } catch (NumberFormatException e) {
379 errPw.println("ims set-ims-service requires an integer as a SLOT_ID.");
380 return -1;
381 }
382 break;
383 }
384 case "-c": {
385 isCarrierService = true;
386 break;
387 }
388 case "-d": {
389 isCarrierService = false;
390 break;
391 }
392 }
393 }
394 // Mandatory param, either -c or -d
395 if (isCarrierService == null) {
396 errPw.println("ims set-ims-service requires either \"-c\" or \"-d\" to be set.");
397 return -1;
398 }
399
400 String result;
401 try {
402 result = mInterface.getImsService(slotId, isCarrierService);
403 } catch (RemoteException e) {
404 return -1;
405 }
406 if (VDBG) {
407 Log.v(LOG_TAG, "ims get-ims-service -s " + slotId + " "
408 + (isCarrierService ? "-c " : "-d ") + ", returned: " + result);
409 }
410 getOutPrintWriter().println(result);
411 return 0;
412 }
413
414 private int handleEnableIms() {
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700415 int slotId = getDefaultSlot();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700416 String opt;
417 while ((opt = getNextOption()) != null) {
418 switch (opt) {
419 case "-s": {
420 try {
421 slotId = Integer.parseInt(getNextArgRequired());
422 } catch (NumberFormatException e) {
423 getErrPrintWriter().println("ims enable requires an integer as a SLOT_ID.");
424 return -1;
425 }
426 break;
427 }
428 }
429 }
430 try {
431 mInterface.enableIms(slotId);
432 } catch (RemoteException e) {
433 return -1;
434 }
435 if (VDBG) {
436 Log.v(LOG_TAG, "ims enable -s " + slotId);
437 }
438 return 0;
439 }
440
441 private int handleDisableIms() {
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700442 int slotId = getDefaultSlot();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700443 String opt;
444 while ((opt = getNextOption()) != null) {
445 switch (opt) {
446 case "-s": {
447 try {
448 slotId = Integer.parseInt(getNextArgRequired());
449 } catch (NumberFormatException e) {
450 getErrPrintWriter().println(
451 "ims disable requires an integer as a SLOT_ID.");
452 return -1;
453 }
454 break;
455 }
456 }
457 }
458 try {
459 mInterface.disableIms(slotId);
460 } catch (RemoteException e) {
461 return -1;
462 }
463 if (VDBG) {
464 Log.v(LOG_TAG, "ims disable -s " + slotId);
465 }
466 return 0;
467 }
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700468
469 private int getDefaultSlot() {
470 int slotId = SubscriptionManager.getDefaultVoicePhoneId();
471 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX
472 || slotId == SubscriptionManager.DEFAULT_PHONE_INDEX) {
473 // If there is no default, default to slot 0.
474 slotId = DEFAULT_PHONE_ID;
475 }
476 return slotId;
477 }
sqian2fff4a32018-11-05 14:18:37 -0800478
479 private int handleSmsCommand() {
480 String arg = getNextArg();
481 if (arg == null) {
482 onHelpSms();
483 return 0;
484 }
485
486 try {
487 switch (arg) {
488 case SMS_GET_APPS: {
489 return handleSmsGetApps();
490 }
491 case SMS_GET_DEFAULT_APP: {
492 return handleSmsGetDefaultApp();
493 }
494 case SMS_SET_DEFAULT_APP: {
495 return handleSmsSetDefaultApp();
496 }
497 default:
498 getErrPrintWriter().println("Unknown command " + arg);
499 }
500 } catch (RemoteException e) {
501 getErrPrintWriter().println("RemoteException: " + e.getMessage());
502 }
503
504 return -1;
505 }
506
507 private int maybeParseUserIdArg() {
508 int userId = UserHandle.USER_SYSTEM;
509 String opt;
510 while ((opt = getNextOption()) != null) {
511 switch (opt) {
512 case "--user": {
513 try {
514 userId = Integer.parseInt(getNextArgRequired());
515 } catch (NumberFormatException e) {
516 getErrPrintWriter().println("Invalid user ID for --user");
517 return -1;
518 }
519 break;
520 }
521 }
522 }
523 return userId;
524 }
525
526 private int handleSmsGetApps() throws RemoteException {
527 final int userId = maybeParseUserIdArg();
528 if (userId < 0) {
529 return -1;
530 }
531
532 for (String packageName : mInterface.getSmsApps(userId)) {
533 getOutPrintWriter().println(packageName);
534 }
535 return 0;
536 }
537
538 private int handleSmsGetDefaultApp() throws RemoteException {
539 final int userId = maybeParseUserIdArg();
540 if (userId < 0) {
541 return -1;
542 }
543
544 getOutPrintWriter().println(mInterface.getDefaultSmsApp(userId));
545 return 0;
546 }
547
548 private int handleSmsSetDefaultApp() throws RemoteException {
549 final int userId = maybeParseUserIdArg();
550 if (userId < 0) {
551 return -1;
552 }
553
554 String packageName = getNextArgRequired();
555 mInterface.setDefaultSmsApp(userId, packageName);
556 getOutPrintWriter().println("SMS app set to " + mInterface.getDefaultSmsApp(userId));
557 return 0;
558 }
Hall Liud892bec2018-11-30 14:51:45 -0800559
560 private boolean checkShellUid() {
Hall Liu2ddfc7e2018-12-06 13:09:45 -0800561 // adb can run as root or as shell, depending on whether the device is rooted.
562 return Binder.getCallingUid() == Process.SHELL_UID
563 || Binder.getCallingUid() == Process.ROOT_UID;
Hall Liud892bec2018-11-30 14:51:45 -0800564 }
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700565}