blob: ffae9ce6317c606119d4902288222284e8f24988 [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
2 * Copyright (C) 2006 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
19import android.app.ActivityManager;
20import android.app.AppOpsManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070021import android.content.Context;
22import android.content.Intent;
23import android.net.ConnectivityManager;
24import android.net.Uri;
25import android.os.AsyncResult;
26import android.os.Binder;
27import android.os.Bundle;
28import android.os.Handler;
29import android.os.Looper;
30import android.os.Message;
31import android.os.Process;
32import android.os.ServiceManager;
33import android.os.UserHandle;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070034import android.telephony.CellInfo;
Jake Hambye994d462014-02-03 13:10:13 -080035import android.telephony.NeighboringCellInfo;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070036import android.telephony.ServiceState;
37import android.text.TextUtils;
38import android.util.Log;
Jake Hambye994d462014-02-03 13:10:13 -080039import android.util.Pair;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070040
Shishir Agrawal566b7612013-10-28 14:41:00 -070041import com.android.internal.telephony.CallManager;
42import com.android.internal.telephony.CommandException;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070043import com.android.internal.telephony.DefaultPhoneNotifier;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070044import com.android.internal.telephony.ITelephony;
Jake Hambye994d462014-02-03 13:10:13 -080045import com.android.internal.telephony.IccCard;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070046import com.android.internal.telephony.Phone;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070047import com.android.internal.telephony.PhoneConstants;
Shishir Agrawal566b7612013-10-28 14:41:00 -070048import com.android.internal.telephony.uicc.IccIoResult;
49import com.android.internal.telephony.uicc.IccUtils;
50import com.android.internal.telephony.uicc.UiccController;
Jake Hambye994d462014-02-03 13:10:13 -080051import com.android.internal.util.HexDump;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070052
Santos Cordon7d4ddf62013-07-10 11:58:08 -070053import java.util.ArrayList;
Jake Hambye994d462014-02-03 13:10:13 -080054import java.util.List;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070055
56/**
57 * Implementation of the ITelephony interface.
58 */
59public class PhoneInterfaceManager extends ITelephony.Stub {
60 private static final String LOG_TAG = "PhoneInterfaceManager";
61 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
62 private static final boolean DBG_LOC = false;
63
64 // Message codes used with mMainThreadHandler
65 private static final int CMD_HANDLE_PIN_MMI = 1;
66 private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
67 private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
68 private static final int CMD_ANSWER_RINGING_CALL = 4;
69 private static final int CMD_END_CALL = 5; // not used yet
70 private static final int CMD_SILENCE_RINGER = 6;
Shishir Agrawal566b7612013-10-28 14:41:00 -070071 private static final int CMD_TRANSMIT_APDU = 7;
72 private static final int EVENT_TRANSMIT_APDU_DONE = 8;
73 private static final int CMD_OPEN_CHANNEL = 9;
74 private static final int EVENT_OPEN_CHANNEL_DONE = 10;
75 private static final int CMD_CLOSE_CHANNEL = 11;
76 private static final int EVENT_CLOSE_CHANNEL_DONE = 12;
Jake Hambye994d462014-02-03 13:10:13 -080077 private static final int CMD_NV_READ_ITEM = 13;
78 private static final int EVENT_NV_READ_ITEM_DONE = 14;
79 private static final int CMD_NV_WRITE_ITEM = 15;
80 private static final int EVENT_NV_WRITE_ITEM_DONE = 16;
81 private static final int CMD_NV_WRITE_CDMA_PRL = 17;
82 private static final int EVENT_NV_WRITE_CDMA_PRL_DONE = 18;
83 private static final int CMD_NV_RESET_CONFIG = 19;
84 private static final int EVENT_NV_RESET_CONFIG_DONE = 20;
Jake Hamby7c27be32014-03-03 13:25:59 -080085 private static final int CMD_GET_PREFERRED_NETWORK_TYPE = 21;
86 private static final int EVENT_GET_PREFERRED_NETWORK_TYPE_DONE = 22;
87 private static final int CMD_SET_PREFERRED_NETWORK_TYPE = 23;
88 private static final int EVENT_SET_PREFERRED_NETWORK_TYPE_DONE = 24;
Shishir Agrawal566b7612013-10-28 14:41:00 -070089
Santos Cordon7d4ddf62013-07-10 11:58:08 -070090
91 /** The singleton instance. */
92 private static PhoneInterfaceManager sInstance;
93
94 PhoneGlobals mApp;
95 Phone mPhone;
96 CallManager mCM;
97 AppOpsManager mAppOps;
98 MainThreadHandler mMainThreadHandler;
Santos Cordon406c0342013-08-28 00:07:47 -070099 CallHandlerServiceProxy mCallHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700100
101 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -0700102 * A request object to use for transmitting data to an ICC.
103 */
104 private static final class IccAPDUArgument {
105 public int channel, cla, command, p1, p2, p3;
106 public String data;
107
108 public IccAPDUArgument(int channel, int cla, int command,
109 int p1, int p2, int p3, String data) {
110 this.channel = channel;
111 this.cla = cla;
112 this.command = command;
113 this.p1 = p1;
114 this.p2 = p2;
115 this.p3 = p3;
116 this.data = data;
117 }
118 }
119
120 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700121 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
122 * request after sending. The main thread will notify the request when it is complete.
123 */
124 private static final class MainThreadRequest {
125 /** The argument to use for the request */
126 public Object argument;
127 /** The result of the request that is run on the main thread */
128 public Object result;
129
130 public MainThreadRequest(Object argument) {
131 this.argument = argument;
132 }
133 }
134
135 /**
136 * A handler that processes messages on the main thread in the phone process. Since many
137 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
138 * inbound binder threads to the main thread in the phone process. The Binder thread
139 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
140 * on, which will be notified when the operation completes and will contain the result of the
141 * request.
142 *
143 * <p>If a MainThreadRequest object is provided in the msg.obj field,
144 * note that request.result must be set to something non-null for the calling thread to
145 * unblock.
146 */
147 private final class MainThreadHandler extends Handler {
148 @Override
149 public void handleMessage(Message msg) {
150 MainThreadRequest request;
151 Message onCompleted;
152 AsyncResult ar;
153
154 switch (msg.what) {
155 case CMD_HANDLE_PIN_MMI:
156 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800157 request.result = mPhone.handlePinMmi((String) request.argument);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700158 // Wake up the requesting thread
159 synchronized (request) {
160 request.notifyAll();
161 }
162 break;
163
164 case CMD_HANDLE_NEIGHBORING_CELL:
165 request = (MainThreadRequest) msg.obj;
166 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
167 request);
168 mPhone.getNeighboringCids(onCompleted);
169 break;
170
171 case EVENT_NEIGHBORING_CELL_DONE:
172 ar = (AsyncResult) msg.obj;
173 request = (MainThreadRequest) ar.userObj;
174 if (ar.exception == null && ar.result != null) {
175 request.result = ar.result;
176 } else {
177 // create an empty list to notify the waiting thread
Jake Hambye994d462014-02-03 13:10:13 -0800178 request.result = new ArrayList<NeighboringCellInfo>(0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700179 }
180 // Wake up the requesting thread
181 synchronized (request) {
182 request.notifyAll();
183 }
184 break;
185
186 case CMD_ANSWER_RINGING_CALL:
187 answerRingingCallInternal();
188 break;
189
190 case CMD_SILENCE_RINGER:
191 silenceRingerInternal();
192 break;
193
194 case CMD_END_CALL:
195 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800196 boolean hungUp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700197 int phoneType = mPhone.getPhoneType();
198 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
199 // CDMA: If the user presses the Power button we treat it as
200 // ending the complete call session
201 hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
202 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
203 // GSM: End the call as per the Phone state
204 hungUp = PhoneUtils.hangup(mCM);
205 } else {
206 throw new IllegalStateException("Unexpected phone type: " + phoneType);
207 }
208 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
209 request.result = hungUp;
210 // Wake up the requesting thread
211 synchronized (request) {
212 request.notifyAll();
213 }
214 break;
215
Shishir Agrawal566b7612013-10-28 14:41:00 -0700216 case CMD_TRANSMIT_APDU:
217 request = (MainThreadRequest) msg.obj;
218 IccAPDUArgument argument = (IccAPDUArgument) request.argument;
219 onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_DONE, request);
220 UiccController.getInstance().getUiccCard().iccTransmitApduLogicalChannel(
221 argument.channel, argument.cla, argument.command,
222 argument.p1, argument.p2, argument.p3, argument.data,
223 onCompleted);
224 break;
225
226 case EVENT_TRANSMIT_APDU_DONE:
227 ar = (AsyncResult) msg.obj;
228 request = (MainThreadRequest) ar.userObj;
229 if (ar.exception == null && ar.result != null) {
230 request.result = ar.result;
231 } else {
232 request.result = new IccIoResult(0x6F, 0, (byte[])null);
233 if (ar.result == null) {
234 loge("iccTransmitApduLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800235 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700236 loge("iccTransmitApduLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800237 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700238 } else {
239 loge("iccTransmitApduLogicalChannel: Unknown exception");
240 }
241 }
242 synchronized (request) {
243 request.notifyAll();
244 }
245 break;
246
247 case CMD_OPEN_CHANNEL:
248 request = (MainThreadRequest) msg.obj;
249 onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request);
250 UiccController.getInstance().getUiccCard().iccOpenLogicalChannel(
251 (String)request.argument, onCompleted);
252 break;
253
254 case EVENT_OPEN_CHANNEL_DONE:
255 ar = (AsyncResult) msg.obj;
256 request = (MainThreadRequest) ar.userObj;
257 if (ar.exception == null && ar.result != null) {
Jake Hambye994d462014-02-03 13:10:13 -0800258 request.result = ((int[]) ar.result)[0];
Shishir Agrawal566b7612013-10-28 14:41:00 -0700259 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800260 request.result = -1;
Shishir Agrawal566b7612013-10-28 14:41:00 -0700261 if (ar.result == null) {
262 loge("iccOpenLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800263 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700264 loge("iccOpenLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800265 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700266 } else {
267 loge("iccOpenLogicalChannel: Unknown exception");
268 }
269 }
270 synchronized (request) {
271 request.notifyAll();
272 }
273 break;
274
275 case CMD_CLOSE_CHANNEL:
276 request = (MainThreadRequest) msg.obj;
277 onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE,
278 request);
279 UiccController.getInstance().getUiccCard().iccCloseLogicalChannel(
Jake Hambye994d462014-02-03 13:10:13 -0800280 (Integer) request.argument,
Shishir Agrawal566b7612013-10-28 14:41:00 -0700281 onCompleted);
282 break;
283
284 case EVENT_CLOSE_CHANNEL_DONE:
Jake Hambye994d462014-02-03 13:10:13 -0800285 handleNullReturnEvent(msg, "iccCloseLogicalChannel");
286 break;
287
288 case CMD_NV_READ_ITEM:
289 request = (MainThreadRequest) msg.obj;
290 onCompleted = obtainMessage(EVENT_NV_READ_ITEM_DONE, request);
291 mPhone.nvReadItem((Integer) request.argument, onCompleted);
292 break;
293
294 case EVENT_NV_READ_ITEM_DONE:
Shishir Agrawal566b7612013-10-28 14:41:00 -0700295 ar = (AsyncResult) msg.obj;
296 request = (MainThreadRequest) ar.userObj;
Jake Hambye994d462014-02-03 13:10:13 -0800297 if (ar.exception == null && ar.result != null) {
298 request.result = ar.result; // String
Shishir Agrawal566b7612013-10-28 14:41:00 -0700299 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800300 request.result = "";
301 if (ar.result == null) {
302 loge("nvReadItem: Empty response");
303 } else if (ar.exception instanceof CommandException) {
304 loge("nvReadItem: CommandException: " +
305 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700306 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800307 loge("nvReadItem: Unknown exception");
Shishir Agrawal566b7612013-10-28 14:41:00 -0700308 }
309 }
310 synchronized (request) {
311 request.notifyAll();
312 }
313 break;
314
Jake Hambye994d462014-02-03 13:10:13 -0800315 case CMD_NV_WRITE_ITEM:
316 request = (MainThreadRequest) msg.obj;
317 onCompleted = obtainMessage(EVENT_NV_WRITE_ITEM_DONE, request);
318 Pair<Integer, String> idValue = (Pair<Integer, String>) request.argument;
319 mPhone.nvWriteItem(idValue.first, idValue.second, onCompleted);
320 break;
321
322 case EVENT_NV_WRITE_ITEM_DONE:
323 handleNullReturnEvent(msg, "nvWriteItem");
324 break;
325
326 case CMD_NV_WRITE_CDMA_PRL:
327 request = (MainThreadRequest) msg.obj;
328 onCompleted = obtainMessage(EVENT_NV_WRITE_CDMA_PRL_DONE, request);
329 mPhone.nvWriteCdmaPrl((byte[]) request.argument, onCompleted);
330 break;
331
332 case EVENT_NV_WRITE_CDMA_PRL_DONE:
333 handleNullReturnEvent(msg, "nvWriteCdmaPrl");
334 break;
335
336 case CMD_NV_RESET_CONFIG:
337 request = (MainThreadRequest) msg.obj;
338 onCompleted = obtainMessage(EVENT_NV_RESET_CONFIG_DONE, request);
339 mPhone.nvResetConfig((Integer) request.argument, onCompleted);
340 break;
341
342 case EVENT_NV_RESET_CONFIG_DONE:
343 handleNullReturnEvent(msg, "nvResetConfig");
344 break;
345
Jake Hamby7c27be32014-03-03 13:25:59 -0800346 case CMD_GET_PREFERRED_NETWORK_TYPE:
347 request = (MainThreadRequest) msg.obj;
348 onCompleted = obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE_DONE, request);
349 mPhone.getPreferredNetworkType(onCompleted);
350 break;
351
352 case EVENT_GET_PREFERRED_NETWORK_TYPE_DONE:
353 ar = (AsyncResult) msg.obj;
354 request = (MainThreadRequest) ar.userObj;
355 if (ar.exception == null && ar.result != null) {
356 request.result = ar.result; // Integer
357 } else {
358 request.result = -1;
359 if (ar.result == null) {
360 loge("getPreferredNetworkType: Empty response");
361 } else if (ar.exception instanceof CommandException) {
362 loge("getPreferredNetworkType: CommandException: " +
363 ar.exception);
364 } else {
365 loge("getPreferredNetworkType: Unknown exception");
366 }
367 }
368 synchronized (request) {
369 request.notifyAll();
370 }
371 break;
372
373 case CMD_SET_PREFERRED_NETWORK_TYPE:
374 request = (MainThreadRequest) msg.obj;
375 onCompleted = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE_DONE, request);
376 int networkType = (Integer) request.argument;
377 mPhone.setPreferredNetworkType(networkType, onCompleted);
378 break;
379
380 case EVENT_SET_PREFERRED_NETWORK_TYPE_DONE:
381 handleNullReturnEvent(msg, "setPreferredNetworkType");
382 break;
383
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700384 default:
385 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
386 break;
387 }
388 }
Jake Hambye994d462014-02-03 13:10:13 -0800389
390 private void handleNullReturnEvent(Message msg, String command) {
391 AsyncResult ar = (AsyncResult) msg.obj;
392 MainThreadRequest request = (MainThreadRequest) ar.userObj;
393 if (ar.exception == null) {
394 request.result = true;
395 } else {
396 request.result = false;
397 if (ar.exception instanceof CommandException) {
398 loge(command + ": CommandException: " + ar.exception);
399 } else {
400 loge(command + ": Unknown exception");
401 }
402 }
403 synchronized (request) {
404 request.notifyAll();
405 }
406 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700407 }
408
409 /**
410 * Posts the specified command to be executed on the main thread,
411 * waits for the request to complete, and returns the result.
412 * @see #sendRequestAsync
413 */
414 private Object sendRequest(int command, Object argument) {
415 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
416 throw new RuntimeException("This method will deadlock if called from the main thread.");
417 }
418
419 MainThreadRequest request = new MainThreadRequest(argument);
420 Message msg = mMainThreadHandler.obtainMessage(command, request);
421 msg.sendToTarget();
422
423 // Wait for the request to complete
424 synchronized (request) {
425 while (request.result == null) {
426 try {
427 request.wait();
428 } catch (InterruptedException e) {
429 // Do nothing, go back and wait until the request is complete
430 }
431 }
432 }
433 return request.result;
434 }
435
436 /**
437 * Asynchronous ("fire and forget") version of sendRequest():
438 * Posts the specified command to be executed on the main thread, and
439 * returns immediately.
440 * @see #sendRequest
441 */
442 private void sendRequestAsync(int command) {
443 mMainThreadHandler.sendEmptyMessage(command);
444 }
445
446 /**
447 * Initialize the singleton PhoneInterfaceManager instance.
448 * This is only done once, at startup, from PhoneApp.onCreate().
449 */
Santos Cordon406c0342013-08-28 00:07:47 -0700450 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
451 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700452 synchronized (PhoneInterfaceManager.class) {
453 if (sInstance == null) {
Santos Cordon406c0342013-08-28 00:07:47 -0700454 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700455 } else {
456 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
457 }
458 return sInstance;
459 }
460 }
461
462 /** Private constructor; @see init() */
Santos Cordon406c0342013-08-28 00:07:47 -0700463 private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
464 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700465 mApp = app;
466 mPhone = phone;
467 mCM = PhoneGlobals.getInstance().mCM;
468 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
469 mMainThreadHandler = new MainThreadHandler();
Santos Cordon406c0342013-08-28 00:07:47 -0700470 mCallHandlerService = callHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700471 publish();
472 }
473
474 private void publish() {
475 if (DBG) log("publish: " + this);
476
477 ServiceManager.addService("phone", this);
478 }
479
480 //
481 // Implementation of the ITelephony interface.
482 //
483
484 public void dial(String number) {
485 if (DBG) log("dial: " + number);
486 // No permission check needed here: This is just a wrapper around the
487 // ACTION_DIAL intent, which is available to any app since it puts up
488 // the UI before it does anything.
489
490 String url = createTelUrl(number);
491 if (url == null) {
492 return;
493 }
494
495 // PENDING: should we just silently fail if phone is offhook or ringing?
496 PhoneConstants.State state = mCM.getState();
497 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
498 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
499 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
500 mApp.startActivity(intent);
501 }
502 }
503
504 public void call(String callingPackage, String number) {
505 if (DBG) log("call: " + number);
506
507 // This is just a wrapper around the ACTION_CALL intent, but we still
508 // need to do a permission check since we're calling startActivity()
509 // from the context of the phone app.
510 enforceCallPermission();
511
512 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
513 != AppOpsManager.MODE_ALLOWED) {
514 return;
515 }
516
517 String url = createTelUrl(number);
518 if (url == null) {
519 return;
520 }
521
522 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
523 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
524 mApp.startActivity(intent);
525 }
526
527 private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
Makoto Onukibcf20992013-09-12 17:59:30 -0700528 boolean showDialpad) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700529 if (!PhoneGlobals.sVoiceCapable) {
530 // Never allow the InCallScreen to appear on data-only devices.
531 return false;
532 }
533 if (isIdle()) {
534 return false;
535 }
536 // If the phone isn't idle then go to the in-call screen
537 long callingId = Binder.clearCallingIdentity();
Santos Cordon406c0342013-08-28 00:07:47 -0700538
Makoto Onukibcf20992013-09-12 17:59:30 -0700539 mCallHandlerService.bringToForeground(showDialpad);
Santos Cordon406c0342013-08-28 00:07:47 -0700540
541 Binder.restoreCallingIdentity(callingId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700542 return true;
543 }
544
545 // Show the in-call screen without specifying the initial dialpad state.
546 public boolean showCallScreen() {
547 return showCallScreenInternal(false, false);
548 }
549
550 // The variation of showCallScreen() that specifies the initial dialpad state.
551 // (Ideally this would be called showCallScreen() too, just with a different
552 // signature, but AIDL doesn't allow that.)
553 public boolean showCallScreenWithDialpad(boolean showDialpad) {
554 return showCallScreenInternal(true, showDialpad);
555 }
556
557 /**
558 * End a call based on call state
559 * @return true is a call was ended
560 */
561 public boolean endCall() {
562 enforceCallPermission();
563 return (Boolean) sendRequest(CMD_END_CALL, null);
564 }
565
566 public void answerRingingCall() {
567 if (DBG) log("answerRingingCall...");
568 // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
569 // but that can probably wait till the big TelephonyManager API overhaul.
570 // For now, protect this call with the MODIFY_PHONE_STATE permission.
571 enforceModifyPermission();
572 sendRequestAsync(CMD_ANSWER_RINGING_CALL);
573 }
574
575 /**
576 * Make the actual telephony calls to implement answerRingingCall().
577 * This should only be called from the main thread of the Phone app.
578 * @see #answerRingingCall
579 *
580 * TODO: it would be nice to return true if we answered the call, or
581 * false if there wasn't actually a ringing incoming call, or some
582 * other error occurred. (In other words, pass back the return value
583 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
584 * But that would require calling this method via sendRequest() rather
585 * than sendRequestAsync(), and right now we don't actually *need* that
586 * return value, so let's just return void for now.
587 */
588 private void answerRingingCallInternal() {
589 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
590 if (hasRingingCall) {
591 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
592 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
593 if (hasActiveCall && hasHoldingCall) {
594 // Both lines are in use!
595 // TODO: provide a flag to let the caller specify what
596 // policy to use if both lines are in use. (The current
597 // behavior is hardwired to "answer incoming, end ongoing",
598 // which is how the CALL button is specced to behave.)
599 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
600 return;
601 } else {
602 // answerCall() will automatically hold the current active
603 // call, if there is one.
604 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
605 return;
606 }
607 } else {
608 // No call was ringing.
609 return;
610 }
611 }
612
613 public void silenceRinger() {
614 if (DBG) log("silenceRinger...");
615 // TODO: find a more appropriate permission to check here.
616 // (That can probably wait till the big TelephonyManager API overhaul.
617 // For now, protect this call with the MODIFY_PHONE_STATE permission.)
618 enforceModifyPermission();
619 sendRequestAsync(CMD_SILENCE_RINGER);
620 }
621
622 /**
623 * Internal implemenation of silenceRinger().
624 * This should only be called from the main thread of the Phone app.
625 * @see #silenceRinger
626 */
627 private void silenceRingerInternal() {
628 if ((mCM.getState() == PhoneConstants.State.RINGING)
629 && mApp.notifier.isRinging()) {
630 // Ringer is actually playing, so silence it.
631 if (DBG) log("silenceRingerInternal: silencing...");
632 mApp.notifier.silenceRinger();
633 }
634 }
635
636 public boolean isOffhook() {
637 return (mCM.getState() == PhoneConstants.State.OFFHOOK);
638 }
639
640 public boolean isRinging() {
641 return (mCM.getState() == PhoneConstants.State.RINGING);
642 }
643
644 public boolean isIdle() {
645 return (mCM.getState() == PhoneConstants.State.IDLE);
646 }
647
648 public boolean isSimPinEnabled() {
649 enforceReadPermission();
650 return (PhoneGlobals.getInstance().isSimPinEnabled());
651 }
652
653 public boolean supplyPin(String pin) {
Wink Saville9de0f752013-10-22 19:04:03 -0700654 int [] resultArray = supplyPinReportResult(pin);
655 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
656 }
657
658 public boolean supplyPuk(String puk, String pin) {
659 int [] resultArray = supplyPukReportResult(puk, pin);
660 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
661 }
662
663 /** {@hide} */
664 public int[] supplyPinReportResult(String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700665 enforceModifyPermission();
666 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
667 checkSimPin.start();
668 return checkSimPin.unlockSim(null, pin);
669 }
670
Wink Saville9de0f752013-10-22 19:04:03 -0700671 /** {@hide} */
672 public int[] supplyPukReportResult(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700673 enforceModifyPermission();
674 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
675 checkSimPuk.start();
676 return checkSimPuk.unlockSim(puk, pin);
677 }
678
679 /**
Wink Saville9de0f752013-10-22 19:04:03 -0700680 * Helper thread to turn async call to SimCard#supplyPin into
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700681 * a synchronous one.
682 */
683 private static class UnlockSim extends Thread {
684
685 private final IccCard mSimCard;
686
687 private boolean mDone = false;
Wink Saville9de0f752013-10-22 19:04:03 -0700688 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
689 private int mRetryCount = -1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700690
691 // For replies from SimCard interface
692 private Handler mHandler;
693
694 // For async handler to identify request type
695 private static final int SUPPLY_PIN_COMPLETE = 100;
696
697 public UnlockSim(IccCard simCard) {
698 mSimCard = simCard;
699 }
700
701 @Override
702 public void run() {
703 Looper.prepare();
704 synchronized (UnlockSim.this) {
705 mHandler = new Handler() {
706 @Override
707 public void handleMessage(Message msg) {
708 AsyncResult ar = (AsyncResult) msg.obj;
709 switch (msg.what) {
710 case SUPPLY_PIN_COMPLETE:
711 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
712 synchronized (UnlockSim.this) {
Wink Saville9de0f752013-10-22 19:04:03 -0700713 mRetryCount = msg.arg1;
714 if (ar.exception != null) {
715 if (ar.exception instanceof CommandException &&
716 ((CommandException)(ar.exception)).getCommandError()
717 == CommandException.Error.PASSWORD_INCORRECT) {
718 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
719 } else {
720 mResult = PhoneConstants.PIN_GENERAL_FAILURE;
721 }
722 } else {
723 mResult = PhoneConstants.PIN_RESULT_SUCCESS;
724 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700725 mDone = true;
726 UnlockSim.this.notifyAll();
727 }
728 break;
729 }
730 }
731 };
732 UnlockSim.this.notifyAll();
733 }
734 Looper.loop();
735 }
736
737 /*
738 * Use PIN or PUK to unlock SIM card
739 *
740 * If PUK is null, unlock SIM card with PIN
741 *
742 * If PUK is not null, unlock SIM card with PUK and set PIN code
743 */
Wink Saville9de0f752013-10-22 19:04:03 -0700744 synchronized int[] unlockSim(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700745
746 while (mHandler == null) {
747 try {
748 wait();
749 } catch (InterruptedException e) {
750 Thread.currentThread().interrupt();
751 }
752 }
753 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
754
755 if (puk == null) {
756 mSimCard.supplyPin(pin, callback);
757 } else {
758 mSimCard.supplyPuk(puk, pin, callback);
759 }
760
761 while (!mDone) {
762 try {
763 Log.d(LOG_TAG, "wait for done");
764 wait();
765 } catch (InterruptedException e) {
766 // Restore the interrupted status
767 Thread.currentThread().interrupt();
768 }
769 }
770 Log.d(LOG_TAG, "done");
Wink Saville9de0f752013-10-22 19:04:03 -0700771 int[] resultArray = new int[2];
772 resultArray[0] = mResult;
773 resultArray[1] = mRetryCount;
774 return resultArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700775 }
776 }
777
778 public void updateServiceLocation() {
779 // No permission check needed here: this call is harmless, and it's
780 // needed for the ServiceState.requestStateUpdate() call (which is
781 // already intentionally exposed to 3rd parties.)
782 mPhone.updateServiceLocation();
783 }
784
785 public boolean isRadioOn() {
786 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
787 }
788
789 public void toggleRadioOnOff() {
790 enforceModifyPermission();
791 mPhone.setRadioPower(!isRadioOn());
792 }
793 public boolean setRadio(boolean turnOn) {
794 enforceModifyPermission();
795 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
796 toggleRadioOnOff();
797 }
798 return true;
799 }
800 public boolean setRadioPower(boolean turnOn) {
801 enforceModifyPermission();
802 mPhone.setRadioPower(turnOn);
803 return true;
804 }
805
806 public boolean enableDataConnectivity() {
807 enforceModifyPermission();
808 ConnectivityManager cm =
809 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
810 cm.setMobileDataEnabled(true);
811 return true;
812 }
813
814 public int enableApnType(String type) {
815 enforceModifyPermission();
816 return mPhone.enableApnType(type);
817 }
818
819 public int disableApnType(String type) {
820 enforceModifyPermission();
821 return mPhone.disableApnType(type);
822 }
823
824 public boolean disableDataConnectivity() {
825 enforceModifyPermission();
826 ConnectivityManager cm =
827 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
828 cm.setMobileDataEnabled(false);
829 return true;
830 }
831
832 public boolean isDataConnectivityPossible() {
833 return mPhone.isDataConnectivityPossible();
834 }
835
836 public boolean handlePinMmi(String dialString) {
837 enforceModifyPermission();
838 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
839 }
840
841 public void cancelMissedCallsNotification() {
842 enforceModifyPermission();
843 mApp.notificationMgr.cancelMissedCallNotification();
844 }
845
846 public int getCallState() {
847 return DefaultPhoneNotifier.convertCallState(mCM.getState());
848 }
849
850 public int getDataState() {
851 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
852 }
853
854 public int getDataActivity() {
855 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
856 }
857
858 @Override
859 public Bundle getCellLocation() {
860 try {
861 mApp.enforceCallingOrSelfPermission(
862 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
863 } catch (SecurityException e) {
864 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
865 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
866 // is the weaker precondition
867 mApp.enforceCallingOrSelfPermission(
868 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
869 }
870
Jake Hambye994d462014-02-03 13:10:13 -0800871 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700872 if (DBG_LOC) log("getCellLocation: is active user");
873 Bundle data = new Bundle();
874 mPhone.getCellLocation().fillInNotifierBundle(data);
875 return data;
876 } else {
877 if (DBG_LOC) log("getCellLocation: suppress non-active user");
878 return null;
879 }
880 }
881
882 @Override
883 public void enableLocationUpdates() {
884 mApp.enforceCallingOrSelfPermission(
885 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
886 mPhone.enableLocationUpdates();
887 }
888
889 @Override
890 public void disableLocationUpdates() {
891 mApp.enforceCallingOrSelfPermission(
892 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
893 mPhone.disableLocationUpdates();
894 }
895
896 @Override
897 @SuppressWarnings("unchecked")
898 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
899 try {
900 mApp.enforceCallingOrSelfPermission(
901 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
902 } catch (SecurityException e) {
903 // If we have ACCESS_FINE_LOCATION permission, skip the check
904 // for ACCESS_COARSE_LOCATION
905 // A failure should throw the SecurityException from
906 // ACCESS_COARSE_LOCATION since this is the weaker precondition
907 mApp.enforceCallingOrSelfPermission(
908 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
909 }
910
911 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
912 callingPackage) != AppOpsManager.MODE_ALLOWED) {
913 return null;
914 }
Jake Hambye994d462014-02-03 13:10:13 -0800915 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700916 if (DBG_LOC) log("getNeighboringCellInfo: is active user");
917
918 ArrayList<NeighboringCellInfo> cells = null;
919
920 try {
921 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
922 CMD_HANDLE_NEIGHBORING_CELL, null);
923 } catch (RuntimeException e) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700924 loge("getNeighboringCellInfo " + e);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700925 }
926 return cells;
927 } else {
928 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
929 return null;
930 }
931 }
932
933
934 @Override
935 public List<CellInfo> getAllCellInfo() {
936 try {
937 mApp.enforceCallingOrSelfPermission(
938 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
939 } catch (SecurityException e) {
940 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
941 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
942 // is the weaker precondition
943 mApp.enforceCallingOrSelfPermission(
944 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
945 }
946
Jake Hambye994d462014-02-03 13:10:13 -0800947 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700948 if (DBG_LOC) log("getAllCellInfo: is active user");
949 return mPhone.getAllCellInfo();
950 } else {
951 if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
952 return null;
953 }
954 }
955
956 public void setCellInfoListRate(int rateInMillis) {
957 mPhone.setCellInfoListRate(rateInMillis);
958 }
959
960 //
961 // Internal helper methods.
962 //
963
Jake Hambye994d462014-02-03 13:10:13 -0800964 private static boolean checkIfCallerIsSelfOrForegroundUser() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700965 boolean ok;
966
967 boolean self = Binder.getCallingUid() == Process.myUid();
968 if (!self) {
969 // Get the caller's user id then clear the calling identity
970 // which will be restored in the finally clause.
971 int callingUser = UserHandle.getCallingUserId();
972 long ident = Binder.clearCallingIdentity();
973
974 try {
975 // With calling identity cleared the current user is the foreground user.
976 int foregroundUser = ActivityManager.getCurrentUser();
977 ok = (foregroundUser == callingUser);
978 if (DBG_LOC) {
979 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
980 + " callingUser=" + callingUser + " ok=" + ok);
981 }
982 } catch (Exception ex) {
983 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
984 ok = false;
985 } finally {
986 Binder.restoreCallingIdentity(ident);
987 }
988 } else {
989 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
990 ok = true;
991 }
992 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
993 return ok;
994 }
995
996 /**
997 * Make sure the caller has the READ_PHONE_STATE permission.
998 *
999 * @throws SecurityException if the caller does not have the required permission
1000 */
1001 private void enforceReadPermission() {
1002 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
1003 }
1004
1005 /**
1006 * Make sure the caller has the MODIFY_PHONE_STATE permission.
1007 *
1008 * @throws SecurityException if the caller does not have the required permission
1009 */
1010 private void enforceModifyPermission() {
1011 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
1012 }
1013
1014 /**
1015 * Make sure the caller has the CALL_PHONE permission.
1016 *
1017 * @throws SecurityException if the caller does not have the required permission
1018 */
1019 private void enforceCallPermission() {
1020 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
1021 }
1022
Shishir Agrawal566b7612013-10-28 14:41:00 -07001023 /**
1024 * Make sure the caller has SIM_COMMUNICATION permission.
1025 *
1026 * @throws SecurityException if the caller does not have the required permission.
1027 */
1028 private void enforceSimCommunicationPermission() {
1029 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.SIM_COMMUNICATION, null);
1030 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001031
1032 private String createTelUrl(String number) {
1033 if (TextUtils.isEmpty(number)) {
1034 return null;
1035 }
1036
Jake Hambye994d462014-02-03 13:10:13 -08001037 return "tel:" + number;
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001038 }
1039
Jake Hambye994d462014-02-03 13:10:13 -08001040 private static void log(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001041 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
1042 }
1043
Jake Hambye994d462014-02-03 13:10:13 -08001044 private static void loge(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001045 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
1046 }
1047
1048 public int getActivePhoneType() {
1049 return mPhone.getPhoneType();
1050 }
1051
1052 /**
1053 * Returns the CDMA ERI icon index to display
1054 */
1055 public int getCdmaEriIconIndex() {
1056 return mPhone.getCdmaEriIconIndex();
1057 }
1058
1059 /**
1060 * Returns the CDMA ERI icon mode,
1061 * 0 - ON
1062 * 1 - FLASHING
1063 */
1064 public int getCdmaEriIconMode() {
1065 return mPhone.getCdmaEriIconMode();
1066 }
1067
1068 /**
1069 * Returns the CDMA ERI text,
1070 */
1071 public String getCdmaEriText() {
1072 return mPhone.getCdmaEriText();
1073 }
1074
1075 /**
1076 * Returns true if CDMA provisioning needs to run.
1077 */
1078 public boolean needsOtaServiceProvisioning() {
1079 return mPhone.needsOtaServiceProvisioning();
1080 }
1081
1082 /**
1083 * Returns the unread count of voicemails
1084 */
1085 public int getVoiceMessageCount() {
1086 return mPhone.getVoiceMessageCount();
1087 }
1088
1089 /**
1090 * Returns the data network type
1091 *
1092 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
1093 */
1094 @Override
1095 public int getNetworkType() {
1096 return mPhone.getServiceState().getDataNetworkType();
1097 }
1098
1099 /**
1100 * Returns the data network type
1101 */
1102 @Override
1103 public int getDataNetworkType() {
1104 return mPhone.getServiceState().getDataNetworkType();
1105 }
1106
1107 /**
1108 * Returns the data network type
1109 */
1110 @Override
1111 public int getVoiceNetworkType() {
1112 return mPhone.getServiceState().getVoiceNetworkType();
1113 }
1114
1115 /**
1116 * @return true if a ICC card is present
1117 */
1118 public boolean hasIccCard() {
1119 return mPhone.getIccCard().hasIccCard();
1120 }
1121
1122 /**
1123 * Return if the current radio is LTE on CDMA. This
1124 * is a tri-state return value as for a period of time
1125 * the mode may be unknown.
1126 *
1127 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
Jake Hambye994d462014-02-03 13:10:13 -08001128 * or {@link Phone#LTE_ON_CDMA_TRUE}
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001129 */
1130 public int getLteOnCdmaMode() {
1131 return mPhone.getLteOnCdmaMode();
1132 }
Shishir Agrawal566b7612013-10-28 14:41:00 -07001133
1134 @Override
1135 public int iccOpenLogicalChannel(String AID) {
1136 enforceSimCommunicationPermission();
1137
1138 if (DBG) log("iccOpenLogicalChannel: " + AID);
1139 Integer channel = (Integer)sendRequest(CMD_OPEN_CHANNEL, AID);
1140 if (DBG) log("iccOpenLogicalChannel: " + channel);
Jake Hambye994d462014-02-03 13:10:13 -08001141 return channel;
Shishir Agrawal566b7612013-10-28 14:41:00 -07001142 }
1143
1144 @Override
1145 public boolean iccCloseLogicalChannel(int channel) {
1146 enforceSimCommunicationPermission();
1147
1148 if (DBG) log("iccCloseLogicalChannel: " + channel);
1149 if (channel < 0) {
1150 return false;
1151 }
Jake Hambye994d462014-02-03 13:10:13 -08001152 Boolean success = (Boolean)sendRequest(CMD_CLOSE_CHANNEL, channel);
Shishir Agrawal566b7612013-10-28 14:41:00 -07001153 if (DBG) log("iccCloseLogicalChannel: " + success);
1154 return success;
1155 }
1156
1157 @Override
1158 public String iccTransmitApduLogicalChannel(int channel, int cla,
1159 int command, int p1, int p2, int p3, String data) {
1160 enforceSimCommunicationPermission();
1161
1162 if (DBG) {
1163 log("iccTransmitApduLogicalChannel: chnl=" + channel + " cla=" + cla +
1164 " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 +
1165 " data=" + data);
1166 }
1167
1168 if (channel < 0) {
1169 return "";
1170 }
1171
1172 IccIoResult response = (IccIoResult)sendRequest(CMD_TRANSMIT_APDU,
1173 new IccAPDUArgument(channel, cla, command, p1, p2, p3, data));
1174 if (DBG) log("iccTransmitApduLogicalChannel: " + response);
1175
1176 // If the payload is null, there was an error. Indicate that by returning
1177 // an empty string.
1178 if (response.payload == null) {
1179 return "";
1180 }
1181
1182 // Append the returned status code to the end of the response payload.
1183 String s = Integer.toHexString(
1184 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1185 s = IccUtils.bytesToHexString(response.payload) + s;
1186 return s;
1187 }
Jake Hambye994d462014-02-03 13:10:13 -08001188
1189 /**
1190 * Read one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1191 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1192 *
1193 * @param itemID the ID of the item to read
1194 * @return the NV item as a String, or null on error.
1195 */
1196 @Override
1197 public String nvReadItem(int itemID) {
1198 enforceModifyPermission();
1199 if (DBG) log("nvReadItem: item " + itemID);
1200 String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID);
1201 if (DBG) log("nvReadItem: item " + itemID + " is \"" + value + '"');
1202 return value;
1203 }
1204
1205 /**
1206 * Write one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1207 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1208 *
1209 * @param itemID the ID of the item to read
1210 * @param itemValue the value to write, as a String
1211 * @return true on success; false on any failure
1212 */
1213 @Override
1214 public boolean nvWriteItem(int itemID, String itemValue) {
1215 enforceModifyPermission();
1216 if (DBG) log("nvWriteItem: item " + itemID + " value \"" + itemValue + '"');
1217 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_ITEM,
1218 new Pair<Integer, String>(itemID, itemValue));
1219 if (DBG) log("nvWriteItem: item " + itemID + ' ' + (success ? "ok" : "fail"));
1220 return success;
1221 }
1222
1223 /**
1224 * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
1225 * Used for device configuration by some CDMA operators.
1226 *
1227 * @param preferredRoamingList byte array containing the new PRL
1228 * @return true on success; false on any failure
1229 */
1230 @Override
1231 public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
1232 enforceModifyPermission();
1233 if (DBG) log("nvWriteCdmaPrl: value: " + HexDump.toHexString(preferredRoamingList));
1234 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_CDMA_PRL, preferredRoamingList);
1235 if (DBG) log("nvWriteCdmaPrl: " + (success ? "ok" : "fail"));
1236 return success;
1237 }
1238
1239 /**
1240 * Perform the specified type of NV config reset.
1241 * Used for device configuration by some CDMA operators.
1242 *
1243 * @param resetType the type of reset to perform (1 == factory reset; 2 == NV-only reset)
1244 * @return true on success; false on any failure
1245 */
1246 @Override
1247 public boolean nvResetConfig(int resetType) {
1248 enforceModifyPermission();
1249 if (DBG) log("nvResetConfig: type " + resetType);
1250 Boolean success = (Boolean) sendRequest(CMD_NV_RESET_CONFIG, resetType);
1251 if (DBG) log("nvResetConfig: type " + resetType + ' ' + (success ? "ok" : "fail"));
1252 return success;
1253 }
Jake Hamby7c27be32014-03-03 13:25:59 -08001254
1255 /**
1256 * Get the preferred network type.
1257 * Used for device configuration by some CDMA operators.
1258 *
1259 * @return the preferred network type, defined in RILConstants.java.
1260 */
1261 @Override
1262 public int getPreferredNetworkType() {
1263 enforceModifyPermission();
1264 if (DBG) log("getPreferredNetworkType");
1265 int[] result = (int[]) sendRequest(CMD_GET_PREFERRED_NETWORK_TYPE, null);
1266 int networkType = (result != null ? result[0] : -1);
1267 if (DBG) log("getPreferredNetworkType: " + networkType);
1268 return networkType;
1269 }
1270
1271 /**
1272 * Set the preferred network type.
1273 * Used for device configuration by some CDMA operators.
1274 *
1275 * @param networkType the preferred network type, defined in RILConstants.java.
1276 * @return true on success; false on any failure.
1277 */
1278 @Override
1279 public boolean setPreferredNetworkType(int networkType) {
1280 enforceModifyPermission();
1281 if (DBG) log("setPreferredNetworkType: type " + networkType);
1282 Boolean success = (Boolean) sendRequest(CMD_SET_PREFERRED_NETWORK_TYPE, networkType);
1283 if (DBG) log("setPreferredNetworkType: " + (success ? "ok" : "fail"));
1284 return success;
1285 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001286}