blob: d8d1717f839be9412772230808fc63f8148f03c3 [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;
23import android.telephony.SubscriptionManager;
sqian9d4df8b2019-01-15 18:32:07 -080024import android.telephony.emergency.EmergencyNumber;
Brad Ebinger4dc095a2018-04-03 15:17:52 -070025import android.util.Log;
26
27import com.android.internal.telephony.ITelephony;
sqian9d4df8b2019-01-15 18:32:07 -080028import com.android.internal.telephony.emergency.EmergencyNumberTracker;
Brad Ebinger4dc095a2018-04-03 15:17:52 -070029
30import java.io.PrintWriter;
sqian9d4df8b2019-01-15 18:32:07 -080031import java.util.ArrayList;
Brad Ebinger4dc095a2018-04-03 15:17:52 -070032
33/**
34 * Takes actions based on the adb commands given by "adb shell cmd phone ...". Be careful, no
35 * permission checks have been done before onCommand was called. Make sure any commands processed
36 * here also contain the appropriate permissions checks.
37 */
38
39public class TelephonyShellCommand extends ShellCommand {
40
41 private static final String LOG_TAG = "TelephonyShellCommand";
42 // Don't commit with this true.
43 private static final boolean VDBG = true;
Brad Ebinger0aa2f242018-04-12 09:49:23 -070044 private static final int DEFAULT_PHONE_ID = 0;
Brad Ebinger4dc095a2018-04-03 15:17:52 -070045
46 private static final String IMS_SUBCOMMAND = "ims";
Hall Liud892bec2018-11-30 14:51:45 -080047 private static final String NUMBER_VERIFICATION_SUBCOMMAND = "numverify";
sqian9d4df8b2019-01-15 18:32:07 -080048 private static final String EMERGENCY_NUMBER_TEST_MODE = "emergency-number-test-mode";
Hall Liud892bec2018-11-30 14:51:45 -080049
Brad Ebinger4dc095a2018-04-03 15:17:52 -070050 private static final String IMS_SET_CARRIER_SERVICE = "set-ims-service";
51 private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service";
52 private static final String IMS_ENABLE = "enable";
53 private static final String IMS_DISABLE = "disable";
Tyler Gunn7bcdc742019-10-04 15:56:59 -070054 // Used to disable or enable processing of conference event package data from the network.
55 // This is handy for testing scenarios where CEP data does not exist on a network which does
56 // support CEP data.
57 private static final String IMS_CEP = "conference-event-package";
Brad Ebinger4dc095a2018-04-03 15:17:52 -070058
Hall Liud892bec2018-11-30 14:51:45 -080059 private static final String NUMBER_VERIFICATION_OVERRIDE_PACKAGE = "override-package";
Hall Liuca5af3a2018-12-04 16:58:23 -080060 private static final String NUMBER_VERIFICATION_FAKE_CALL = "fake-call";
Hall Liud892bec2018-11-30 14:51:45 -080061
Brad Ebinger4dc095a2018-04-03 15:17:52 -070062 // Take advantage of existing methods that already contain permissions checks when possible.
63 private final ITelephony mInterface;
64
65 public TelephonyShellCommand(ITelephony binder) {
66 mInterface = binder;
67 }
68
69 @Override
70 public int onCommand(String cmd) {
71 if (cmd == null) {
72 return handleDefaultCommands(null);
73 }
74
75 switch (cmd) {
76 case IMS_SUBCOMMAND: {
77 return handleImsCommand();
78 }
Hall Liud892bec2018-11-30 14:51:45 -080079 case NUMBER_VERIFICATION_SUBCOMMAND:
80 return handleNumberVerificationCommand();
sqian9d4df8b2019-01-15 18:32:07 -080081 case EMERGENCY_NUMBER_TEST_MODE:
82 return handleEmergencyNumberTestModeCommand();
Brad Ebinger4dc095a2018-04-03 15:17:52 -070083 default: {
84 return handleDefaultCommands(cmd);
85 }
86 }
87 }
88
89 @Override
90 public void onHelp() {
91 PrintWriter pw = getOutPrintWriter();
92 pw.println("Telephony Commands:");
93 pw.println(" help");
94 pw.println(" Print this help text.");
95 pw.println(" ims");
96 pw.println(" IMS Commands.");
sqian9d4df8b2019-01-15 18:32:07 -080097 pw.println(" emergency-number-test-mode");
98 pw.println(" Emergency Number Test Mode Commands.");
Brad Ebinger4dc095a2018-04-03 15:17:52 -070099 onHelpIms();
sqian9d4df8b2019-01-15 18:32:07 -0800100 onHelpEmergencyNumber();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700101 }
102
103 private void onHelpIms() {
104 PrintWriter pw = getOutPrintWriter();
105 pw.println("IMS Commands:");
106 pw.println(" ims set-ims-service [-s SLOT_ID] (-c | -d) PACKAGE_NAME");
107 pw.println(" Sets the ImsService defined in PACKAGE_NAME to to be the bound");
108 pw.println(" ImsService. Options are:");
109 pw.println(" -s: the slot ID that the ImsService should be bound for. If no option");
110 pw.println(" is specified, it will choose the default voice SIM slot.");
111 pw.println(" -c: Override the ImsService defined in the carrier configuration.");
112 pw.println(" -d: Override the ImsService defined in the device overlay.");
113 pw.println(" ims get-ims-service [-s SLOT_ID] [-c | -d]");
114 pw.println(" Gets the package name of the currently defined ImsService.");
115 pw.println(" Options are:");
116 pw.println(" -s: The SIM slot ID for the registered ImsService. If no option");
117 pw.println(" is specified, it will choose the default voice SIM slot.");
118 pw.println(" -c: The ImsService defined as the carrier configured ImsService.");
119 pw.println(" -c: The ImsService defined as the device default ImsService.");
120 pw.println(" ims enable [-s SLOT_ID]");
121 pw.println(" enables IMS for the SIM slot specified, or for the default voice SIM slot");
122 pw.println(" if none is specified.");
123 pw.println(" ims disable [-s SLOT_ID]");
124 pw.println(" disables IMS for the SIM slot specified, or for the default voice SIM");
125 pw.println(" slot if none is specified.");
Tyler Gunn7bcdc742019-10-04 15:56:59 -0700126 pw.println(" ims conference-event-package [enable/disable]");
127 pw.println(" enables or disables handling or network conference event package data.");
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700128 }
129
Hall Liud892bec2018-11-30 14:51:45 -0800130 private void onHelpNumberVerification() {
131 PrintWriter pw = getOutPrintWriter();
132 pw.println("Number verification commands");
133 pw.println(" numverify override-package PACKAGE_NAME;");
134 pw.println(" Set the authorized package for number verification.");
135 pw.println(" Leave the package name blank to reset.");
Hall Liuca5af3a2018-12-04 16:58:23 -0800136 pw.println(" numverify fake-call NUMBER;");
137 pw.println(" Fake an incoming call from NUMBER. This is for testing. Output will be");
138 pw.println(" 1 if the call would have been intercepted, 0 otherwise.");
Hall Liud892bec2018-11-30 14:51:45 -0800139 }
140
sqian9d4df8b2019-01-15 18:32:07 -0800141 private void onHelpEmergencyNumber() {
142 PrintWriter pw = getOutPrintWriter();
143 pw.println("Emergency Number Test Mode Commands:");
144 pw.println(" emergency-number-test-mode ");
145 pw.println(" Add(-a), Clear(-c), Print (-p) or Remove(-r) the emergency number list in"
146 + " the test mode");
147 pw.println(" -a <emergency number address>: add an emergency number address for the"
sqian9121f982019-03-14 19:45:39 -0700148 + " test mode, only allows '0'-'9', '*', '#' or '+'.");
sqian9d4df8b2019-01-15 18:32:07 -0800149 pw.println(" -c: clear the emergency number list in the test mode.");
150 pw.println(" -r <emergency number address>: remove an existing emergency number"
sqian9121f982019-03-14 19:45:39 -0700151 + " address added by the test mode, only allows '0'-'9', '*', '#' or '+'.");
sqian9d4df8b2019-01-15 18:32:07 -0800152 pw.println(" -p: get the full emergency number list in the test mode.");
153 }
154
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700155 private int handleImsCommand() {
156 String arg = getNextArg();
157 if (arg == null) {
158 onHelpIms();
159 return 0;
160 }
161
162 switch (arg) {
163 case IMS_SET_CARRIER_SERVICE: {
164 return handleImsSetServiceCommand();
165 }
166 case IMS_GET_CARRIER_SERVICE: {
167 return handleImsGetServiceCommand();
168 }
169 case IMS_ENABLE: {
170 return handleEnableIms();
171 }
172 case IMS_DISABLE: {
173 return handleDisableIms();
174 }
Tyler Gunn7bcdc742019-10-04 15:56:59 -0700175 case IMS_CEP: {
176 return handleCepChange();
177 }
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700178 }
179
180 return -1;
181 }
182
sqian9d4df8b2019-01-15 18:32:07 -0800183 private int handleEmergencyNumberTestModeCommand() {
184 PrintWriter errPw = getErrPrintWriter();
185 String opt = getNextOption();
186 if (opt == null) {
187 onHelpEmergencyNumber();
188 return 0;
189 }
190
191 switch (opt) {
192 case "-a": {
193 String emergencyNumberCmd = getNextArgRequired();
194 if (emergencyNumberCmd == null
195 || !EmergencyNumber.validateEmergencyNumberAddress(emergencyNumberCmd)) {
sqian9121f982019-03-14 19:45:39 -0700196 errPw.println("An emergency number (only allow '0'-'9', '*', '#' or '+') needs"
sqian9d4df8b2019-01-15 18:32:07 -0800197 + " to be specified after -a in the command ");
198 return -1;
199 }
200 try {
201 mInterface.updateEmergencyNumberListTestMode(
202 EmergencyNumberTracker.ADD_EMERGENCY_NUMBER_TEST_MODE,
203 new EmergencyNumber(emergencyNumberCmd, "", "",
204 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
205 new ArrayList<String>(),
206 EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST,
207 EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
208 } catch (RemoteException ex) {
209 Log.w(LOG_TAG, "emergency-number-test-mode -a " + emergencyNumberCmd
210 + ", error " + ex.getMessage());
211 errPw.println("Exception: " + ex.getMessage());
212 return -1;
213 }
214 break;
215 }
216 case "-c": {
217 try {
218 mInterface.updateEmergencyNumberListTestMode(
219 EmergencyNumberTracker.RESET_EMERGENCY_NUMBER_TEST_MODE, null);
220 } catch (RemoteException ex) {
221 Log.w(LOG_TAG, "emergency-number-test-mode -c " + "error " + ex.getMessage());
222 errPw.println("Exception: " + ex.getMessage());
223 return -1;
224 }
225 break;
226 }
227 case "-r": {
228 String emergencyNumberCmd = getNextArgRequired();
229 if (emergencyNumberCmd == null
230 || !EmergencyNumber.validateEmergencyNumberAddress(emergencyNumberCmd)) {
sqian9121f982019-03-14 19:45:39 -0700231 errPw.println("An emergency number (only allow '0'-'9', '*', '#' or '+') needs"
sqian9d4df8b2019-01-15 18:32:07 -0800232 + " to be specified after -r in the command ");
233 return -1;
234 }
235 try {
236 mInterface.updateEmergencyNumberListTestMode(
237 EmergencyNumberTracker.REMOVE_EMERGENCY_NUMBER_TEST_MODE,
238 new EmergencyNumber(emergencyNumberCmd, "", "",
239 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
240 new ArrayList<String>(),
241 EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST,
242 EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
243 } catch (RemoteException ex) {
244 Log.w(LOG_TAG, "emergency-number-test-mode -r " + emergencyNumberCmd
245 + ", error " + ex.getMessage());
246 errPw.println("Exception: " + ex.getMessage());
247 return -1;
248 }
249 break;
250 }
251 case "-p": {
252 try {
253 getOutPrintWriter().println(mInterface.getEmergencyNumberListTestMode());
254 } catch (RemoteException ex) {
255 Log.w(LOG_TAG, "emergency-number-test-mode -p " + "error " + ex.getMessage());
256 errPw.println("Exception: " + ex.getMessage());
257 return -1;
258 }
259 break;
260 }
261 default:
262 onHelpEmergencyNumber();
263 break;
264 }
265 return 0;
266 }
267
Hall Liud892bec2018-11-30 14:51:45 -0800268 private int handleNumberVerificationCommand() {
269 String arg = getNextArg();
270 if (arg == null) {
271 onHelpNumberVerification();
272 return 0;
273 }
274
Hall Liuca5af3a2018-12-04 16:58:23 -0800275 if (!checkShellUid()) {
276 return -1;
277 }
278
Hall Liud892bec2018-11-30 14:51:45 -0800279 switch (arg) {
280 case NUMBER_VERIFICATION_OVERRIDE_PACKAGE: {
Hall Liud892bec2018-11-30 14:51:45 -0800281 NumberVerificationManager.overrideAuthorizedPackage(getNextArg());
282 return 0;
283 }
Hall Liuca5af3a2018-12-04 16:58:23 -0800284 case NUMBER_VERIFICATION_FAKE_CALL: {
285 boolean val = NumberVerificationManager.getInstance()
286 .checkIncomingCall(getNextArg());
287 getOutPrintWriter().println(val ? "1" : "0");
288 return 0;
289 }
Hall Liud892bec2018-11-30 14:51:45 -0800290 }
291
292 return -1;
293 }
294
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700295 // ims set-ims-service
296 private int handleImsSetServiceCommand() {
297 PrintWriter errPw = getErrPrintWriter();
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700298 int slotId = getDefaultSlot();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700299 Boolean isCarrierService = null;
300
301 String opt;
302 while ((opt = getNextOption()) != null) {
303 switch (opt) {
304 case "-s": {
305 try {
306 slotId = Integer.parseInt(getNextArgRequired());
307 } catch (NumberFormatException e) {
308 errPw.println("ims set-ims-service requires an integer as a SLOT_ID.");
309 return -1;
310 }
311 break;
312 }
313 case "-c": {
314 isCarrierService = true;
315 break;
316 }
317 case "-d": {
318 isCarrierService = false;
319 break;
320 }
321 }
322 }
323 // Mandatory param, either -c or -d
324 if (isCarrierService == null) {
325 errPw.println("ims set-ims-service requires either \"-c\" or \"-d\" to be set.");
326 return -1;
327 }
328
329 String packageName = getNextArg();
330
331 try {
332 if (packageName == null) {
333 packageName = "";
334 }
335 boolean result = mInterface.setImsService(slotId, isCarrierService, packageName);
336 if (VDBG) {
337 Log.v(LOG_TAG, "ims set-ims-service -s " + slotId + " "
338 + (isCarrierService ? "-c " : "-d ") + packageName + ", result=" + result);
339 }
340 getOutPrintWriter().println(result);
341 } catch (RemoteException e) {
342 Log.w(LOG_TAG, "ims set-ims-service -s " + slotId + " "
343 + (isCarrierService ? "-c " : "-d ") + packageName + ", error"
344 + e.getMessage());
345 errPw.println("Exception: " + e.getMessage());
346 return -1;
347 }
348 return 0;
349 }
350
351 // ims get-ims-service
352 private int handleImsGetServiceCommand() {
353 PrintWriter errPw = getErrPrintWriter();
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700354 int slotId = getDefaultSlot();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700355 Boolean isCarrierService = null;
356
357 String opt;
358 while ((opt = getNextOption()) != null) {
359 switch (opt) {
360 case "-s": {
361 try {
362 slotId = Integer.parseInt(getNextArgRequired());
363 } catch (NumberFormatException e) {
364 errPw.println("ims set-ims-service requires an integer as a SLOT_ID.");
365 return -1;
366 }
367 break;
368 }
369 case "-c": {
370 isCarrierService = true;
371 break;
372 }
373 case "-d": {
374 isCarrierService = false;
375 break;
376 }
377 }
378 }
379 // Mandatory param, either -c or -d
380 if (isCarrierService == null) {
381 errPw.println("ims set-ims-service requires either \"-c\" or \"-d\" to be set.");
382 return -1;
383 }
384
385 String result;
386 try {
387 result = mInterface.getImsService(slotId, isCarrierService);
388 } catch (RemoteException e) {
389 return -1;
390 }
391 if (VDBG) {
392 Log.v(LOG_TAG, "ims get-ims-service -s " + slotId + " "
393 + (isCarrierService ? "-c " : "-d ") + ", returned: " + result);
394 }
395 getOutPrintWriter().println(result);
396 return 0;
397 }
398
399 private int handleEnableIms() {
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700400 int slotId = getDefaultSlot();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700401 String opt;
402 while ((opt = getNextOption()) != null) {
403 switch (opt) {
404 case "-s": {
405 try {
406 slotId = Integer.parseInt(getNextArgRequired());
407 } catch (NumberFormatException e) {
408 getErrPrintWriter().println("ims enable requires an integer as a SLOT_ID.");
409 return -1;
410 }
411 break;
412 }
413 }
414 }
415 try {
416 mInterface.enableIms(slotId);
417 } catch (RemoteException e) {
418 return -1;
419 }
420 if (VDBG) {
421 Log.v(LOG_TAG, "ims enable -s " + slotId);
422 }
423 return 0;
424 }
425
426 private int handleDisableIms() {
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700427 int slotId = getDefaultSlot();
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700428 String opt;
429 while ((opt = getNextOption()) != null) {
430 switch (opt) {
431 case "-s": {
432 try {
433 slotId = Integer.parseInt(getNextArgRequired());
434 } catch (NumberFormatException e) {
435 getErrPrintWriter().println(
436 "ims disable requires an integer as a SLOT_ID.");
437 return -1;
438 }
439 break;
440 }
441 }
442 }
443 try {
444 mInterface.disableIms(slotId);
445 } catch (RemoteException e) {
446 return -1;
447 }
448 if (VDBG) {
449 Log.v(LOG_TAG, "ims disable -s " + slotId);
450 }
451 return 0;
452 }
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700453
Tyler Gunn7bcdc742019-10-04 15:56:59 -0700454 private int handleCepChange() {
455 Log.i(LOG_TAG, "handleCepChange");
456 String opt = getNextArg();
457 if (opt == null) {
458 return -1;
459 }
460 boolean isCepEnabled = opt.equals("enable");
461
462 try {
463 mInterface.setCepEnabled(isCepEnabled);
464 } catch (RemoteException e) {
465 return -1;
466 }
467 return 0;
468 }
469
Brad Ebinger0aa2f242018-04-12 09:49:23 -0700470 private int getDefaultSlot() {
471 int slotId = SubscriptionManager.getDefaultVoicePhoneId();
472 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX
473 || slotId == SubscriptionManager.DEFAULT_PHONE_INDEX) {
474 // If there is no default, default to slot 0.
475 slotId = DEFAULT_PHONE_ID;
476 }
477 return slotId;
478 }
sqian2fff4a32018-11-05 14:18:37 -0800479
Hall Liud892bec2018-11-30 14:51:45 -0800480 private boolean checkShellUid() {
Hall Liu2ddfc7e2018-12-06 13:09:45 -0800481 // adb can run as root or as shell, depending on whether the device is rooted.
482 return Binder.getCallingUid() == Process.SHELL_UID
483 || Binder.getCallingUid() == Process.ROOT_UID;
Hall Liud892bec2018-11-30 14:51:45 -0800484 }
Brad Ebinger4dc095a2018-04-03 15:17:52 -0700485}