blob: 2852ce6ff74400a5255f4f3c9dc33f766e98c05b [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;
85 private static final int CMD_SET_RADIO_MODE = 21;
86 private static final int EVENT_SET_RADIO_MODE_DONE = 22;
Shishir Agrawal566b7612013-10-28 14:41:00 -070087
Santos Cordon7d4ddf62013-07-10 11:58:08 -070088
89 /** The singleton instance. */
90 private static PhoneInterfaceManager sInstance;
91
92 PhoneGlobals mApp;
93 Phone mPhone;
94 CallManager mCM;
95 AppOpsManager mAppOps;
96 MainThreadHandler mMainThreadHandler;
Santos Cordon406c0342013-08-28 00:07:47 -070097 CallHandlerServiceProxy mCallHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070098
99 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -0700100 * A request object to use for transmitting data to an ICC.
101 */
102 private static final class IccAPDUArgument {
103 public int channel, cla, command, p1, p2, p3;
104 public String data;
105
106 public IccAPDUArgument(int channel, int cla, int command,
107 int p1, int p2, int p3, String data) {
108 this.channel = channel;
109 this.cla = cla;
110 this.command = command;
111 this.p1 = p1;
112 this.p2 = p2;
113 this.p3 = p3;
114 this.data = data;
115 }
116 }
117
118 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700119 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
120 * request after sending. The main thread will notify the request when it is complete.
121 */
122 private static final class MainThreadRequest {
123 /** The argument to use for the request */
124 public Object argument;
125 /** The result of the request that is run on the main thread */
126 public Object result;
127
128 public MainThreadRequest(Object argument) {
129 this.argument = argument;
130 }
131 }
132
133 /**
134 * A handler that processes messages on the main thread in the phone process. Since many
135 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
136 * inbound binder threads to the main thread in the phone process. The Binder thread
137 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
138 * on, which will be notified when the operation completes and will contain the result of the
139 * request.
140 *
141 * <p>If a MainThreadRequest object is provided in the msg.obj field,
142 * note that request.result must be set to something non-null for the calling thread to
143 * unblock.
144 */
145 private final class MainThreadHandler extends Handler {
146 @Override
147 public void handleMessage(Message msg) {
148 MainThreadRequest request;
149 Message onCompleted;
150 AsyncResult ar;
151
152 switch (msg.what) {
153 case CMD_HANDLE_PIN_MMI:
154 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800155 request.result = mPhone.handlePinMmi((String) request.argument);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700156 // Wake up the requesting thread
157 synchronized (request) {
158 request.notifyAll();
159 }
160 break;
161
162 case CMD_HANDLE_NEIGHBORING_CELL:
163 request = (MainThreadRequest) msg.obj;
164 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
165 request);
166 mPhone.getNeighboringCids(onCompleted);
167 break;
168
169 case EVENT_NEIGHBORING_CELL_DONE:
170 ar = (AsyncResult) msg.obj;
171 request = (MainThreadRequest) ar.userObj;
172 if (ar.exception == null && ar.result != null) {
173 request.result = ar.result;
174 } else {
175 // create an empty list to notify the waiting thread
Jake Hambye994d462014-02-03 13:10:13 -0800176 request.result = new ArrayList<NeighboringCellInfo>(0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700177 }
178 // Wake up the requesting thread
179 synchronized (request) {
180 request.notifyAll();
181 }
182 break;
183
184 case CMD_ANSWER_RINGING_CALL:
185 answerRingingCallInternal();
186 break;
187
188 case CMD_SILENCE_RINGER:
189 silenceRingerInternal();
190 break;
191
192 case CMD_END_CALL:
193 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800194 boolean hungUp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700195 int phoneType = mPhone.getPhoneType();
196 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
197 // CDMA: If the user presses the Power button we treat it as
198 // ending the complete call session
199 hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
200 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
201 // GSM: End the call as per the Phone state
202 hungUp = PhoneUtils.hangup(mCM);
203 } else {
204 throw new IllegalStateException("Unexpected phone type: " + phoneType);
205 }
206 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
207 request.result = hungUp;
208 // Wake up the requesting thread
209 synchronized (request) {
210 request.notifyAll();
211 }
212 break;
213
Shishir Agrawal566b7612013-10-28 14:41:00 -0700214 case CMD_TRANSMIT_APDU:
215 request = (MainThreadRequest) msg.obj;
216 IccAPDUArgument argument = (IccAPDUArgument) request.argument;
217 onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_DONE, request);
218 UiccController.getInstance().getUiccCard().iccTransmitApduLogicalChannel(
219 argument.channel, argument.cla, argument.command,
220 argument.p1, argument.p2, argument.p3, argument.data,
221 onCompleted);
222 break;
223
224 case EVENT_TRANSMIT_APDU_DONE:
225 ar = (AsyncResult) msg.obj;
226 request = (MainThreadRequest) ar.userObj;
227 if (ar.exception == null && ar.result != null) {
228 request.result = ar.result;
229 } else {
230 request.result = new IccIoResult(0x6F, 0, (byte[])null);
231 if (ar.result == null) {
232 loge("iccTransmitApduLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800233 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700234 loge("iccTransmitApduLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800235 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700236 } else {
237 loge("iccTransmitApduLogicalChannel: Unknown exception");
238 }
239 }
240 synchronized (request) {
241 request.notifyAll();
242 }
243 break;
244
245 case CMD_OPEN_CHANNEL:
246 request = (MainThreadRequest) msg.obj;
247 onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request);
248 UiccController.getInstance().getUiccCard().iccOpenLogicalChannel(
249 (String)request.argument, onCompleted);
250 break;
251
252 case EVENT_OPEN_CHANNEL_DONE:
253 ar = (AsyncResult) msg.obj;
254 request = (MainThreadRequest) ar.userObj;
255 if (ar.exception == null && ar.result != null) {
Jake Hambye994d462014-02-03 13:10:13 -0800256 request.result = ((int[]) ar.result)[0];
Shishir Agrawal566b7612013-10-28 14:41:00 -0700257 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800258 request.result = -1;
Shishir Agrawal566b7612013-10-28 14:41:00 -0700259 if (ar.result == null) {
260 loge("iccOpenLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800261 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700262 loge("iccOpenLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800263 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700264 } else {
265 loge("iccOpenLogicalChannel: Unknown exception");
266 }
267 }
268 synchronized (request) {
269 request.notifyAll();
270 }
271 break;
272
273 case CMD_CLOSE_CHANNEL:
274 request = (MainThreadRequest) msg.obj;
275 onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE,
276 request);
277 UiccController.getInstance().getUiccCard().iccCloseLogicalChannel(
Jake Hambye994d462014-02-03 13:10:13 -0800278 (Integer) request.argument,
Shishir Agrawal566b7612013-10-28 14:41:00 -0700279 onCompleted);
280 break;
281
282 case EVENT_CLOSE_CHANNEL_DONE:
Jake Hambye994d462014-02-03 13:10:13 -0800283 handleNullReturnEvent(msg, "iccCloseLogicalChannel");
284 break;
285
286 case CMD_NV_READ_ITEM:
287 request = (MainThreadRequest) msg.obj;
288 onCompleted = obtainMessage(EVENT_NV_READ_ITEM_DONE, request);
289 mPhone.nvReadItem((Integer) request.argument, onCompleted);
290 break;
291
292 case EVENT_NV_READ_ITEM_DONE:
Shishir Agrawal566b7612013-10-28 14:41:00 -0700293 ar = (AsyncResult) msg.obj;
294 request = (MainThreadRequest) ar.userObj;
Jake Hambye994d462014-02-03 13:10:13 -0800295 if (ar.exception == null && ar.result != null) {
296 request.result = ar.result; // String
Shishir Agrawal566b7612013-10-28 14:41:00 -0700297 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800298 request.result = "";
299 if (ar.result == null) {
300 loge("nvReadItem: Empty response");
301 } else if (ar.exception instanceof CommandException) {
302 loge("nvReadItem: CommandException: " +
303 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700304 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800305 loge("nvReadItem: Unknown exception");
Shishir Agrawal566b7612013-10-28 14:41:00 -0700306 }
307 }
308 synchronized (request) {
309 request.notifyAll();
310 }
311 break;
312
Jake Hambye994d462014-02-03 13:10:13 -0800313 case CMD_NV_WRITE_ITEM:
314 request = (MainThreadRequest) msg.obj;
315 onCompleted = obtainMessage(EVENT_NV_WRITE_ITEM_DONE, request);
316 Pair<Integer, String> idValue = (Pair<Integer, String>) request.argument;
317 mPhone.nvWriteItem(idValue.first, idValue.second, onCompleted);
318 break;
319
320 case EVENT_NV_WRITE_ITEM_DONE:
321 handleNullReturnEvent(msg, "nvWriteItem");
322 break;
323
324 case CMD_NV_WRITE_CDMA_PRL:
325 request = (MainThreadRequest) msg.obj;
326 onCompleted = obtainMessage(EVENT_NV_WRITE_CDMA_PRL_DONE, request);
327 mPhone.nvWriteCdmaPrl((byte[]) request.argument, onCompleted);
328 break;
329
330 case EVENT_NV_WRITE_CDMA_PRL_DONE:
331 handleNullReturnEvent(msg, "nvWriteCdmaPrl");
332 break;
333
334 case CMD_NV_RESET_CONFIG:
335 request = (MainThreadRequest) msg.obj;
336 onCompleted = obtainMessage(EVENT_NV_RESET_CONFIG_DONE, request);
337 mPhone.nvResetConfig((Integer) request.argument, onCompleted);
338 break;
339
340 case EVENT_NV_RESET_CONFIG_DONE:
341 handleNullReturnEvent(msg, "nvResetConfig");
342 break;
343
344 case CMD_SET_RADIO_MODE:
345 request = (MainThreadRequest) msg.obj;
346 onCompleted = obtainMessage(EVENT_SET_RADIO_MODE_DONE, request);
347 mPhone.setRadioMode((Integer) request.argument, onCompleted);
348 break;
349
350 case EVENT_SET_RADIO_MODE_DONE:
351 handleNullReturnEvent(msg, "setRadioMode");
352 break;
353
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700354 default:
355 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
356 break;
357 }
358 }
Jake Hambye994d462014-02-03 13:10:13 -0800359
360 private void handleNullReturnEvent(Message msg, String command) {
361 AsyncResult ar = (AsyncResult) msg.obj;
362 MainThreadRequest request = (MainThreadRequest) ar.userObj;
363 if (ar.exception == null) {
364 request.result = true;
365 } else {
366 request.result = false;
367 if (ar.exception instanceof CommandException) {
368 loge(command + ": CommandException: " + ar.exception);
369 } else {
370 loge(command + ": Unknown exception");
371 }
372 }
373 synchronized (request) {
374 request.notifyAll();
375 }
376 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700377 }
378
379 /**
380 * Posts the specified command to be executed on the main thread,
381 * waits for the request to complete, and returns the result.
382 * @see #sendRequestAsync
383 */
384 private Object sendRequest(int command, Object argument) {
385 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
386 throw new RuntimeException("This method will deadlock if called from the main thread.");
387 }
388
389 MainThreadRequest request = new MainThreadRequest(argument);
390 Message msg = mMainThreadHandler.obtainMessage(command, request);
391 msg.sendToTarget();
392
393 // Wait for the request to complete
394 synchronized (request) {
395 while (request.result == null) {
396 try {
397 request.wait();
398 } catch (InterruptedException e) {
399 // Do nothing, go back and wait until the request is complete
400 }
401 }
402 }
403 return request.result;
404 }
405
406 /**
407 * Asynchronous ("fire and forget") version of sendRequest():
408 * Posts the specified command to be executed on the main thread, and
409 * returns immediately.
410 * @see #sendRequest
411 */
412 private void sendRequestAsync(int command) {
413 mMainThreadHandler.sendEmptyMessage(command);
414 }
415
416 /**
417 * Initialize the singleton PhoneInterfaceManager instance.
418 * This is only done once, at startup, from PhoneApp.onCreate().
419 */
Santos Cordon406c0342013-08-28 00:07:47 -0700420 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
421 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700422 synchronized (PhoneInterfaceManager.class) {
423 if (sInstance == null) {
Santos Cordon406c0342013-08-28 00:07:47 -0700424 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700425 } else {
426 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
427 }
428 return sInstance;
429 }
430 }
431
432 /** Private constructor; @see init() */
Santos Cordon406c0342013-08-28 00:07:47 -0700433 private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
434 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700435 mApp = app;
436 mPhone = phone;
437 mCM = PhoneGlobals.getInstance().mCM;
438 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
439 mMainThreadHandler = new MainThreadHandler();
Santos Cordon406c0342013-08-28 00:07:47 -0700440 mCallHandlerService = callHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700441 publish();
442 }
443
444 private void publish() {
445 if (DBG) log("publish: " + this);
446
447 ServiceManager.addService("phone", this);
448 }
449
450 //
451 // Implementation of the ITelephony interface.
452 //
453
454 public void dial(String number) {
455 if (DBG) log("dial: " + number);
456 // No permission check needed here: This is just a wrapper around the
457 // ACTION_DIAL intent, which is available to any app since it puts up
458 // the UI before it does anything.
459
460 String url = createTelUrl(number);
461 if (url == null) {
462 return;
463 }
464
465 // PENDING: should we just silently fail if phone is offhook or ringing?
466 PhoneConstants.State state = mCM.getState();
467 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
468 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
469 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
470 mApp.startActivity(intent);
471 }
472 }
473
474 public void call(String callingPackage, String number) {
475 if (DBG) log("call: " + number);
476
477 // This is just a wrapper around the ACTION_CALL intent, but we still
478 // need to do a permission check since we're calling startActivity()
479 // from the context of the phone app.
480 enforceCallPermission();
481
482 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
483 != AppOpsManager.MODE_ALLOWED) {
484 return;
485 }
486
487 String url = createTelUrl(number);
488 if (url == null) {
489 return;
490 }
491
492 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
493 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
494 mApp.startActivity(intent);
495 }
496
497 private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
Makoto Onukibcf20992013-09-12 17:59:30 -0700498 boolean showDialpad) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700499 if (!PhoneGlobals.sVoiceCapable) {
500 // Never allow the InCallScreen to appear on data-only devices.
501 return false;
502 }
503 if (isIdle()) {
504 return false;
505 }
506 // If the phone isn't idle then go to the in-call screen
507 long callingId = Binder.clearCallingIdentity();
Santos Cordon406c0342013-08-28 00:07:47 -0700508
Makoto Onukibcf20992013-09-12 17:59:30 -0700509 mCallHandlerService.bringToForeground(showDialpad);
Santos Cordon406c0342013-08-28 00:07:47 -0700510
511 Binder.restoreCallingIdentity(callingId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700512 return true;
513 }
514
515 // Show the in-call screen without specifying the initial dialpad state.
516 public boolean showCallScreen() {
517 return showCallScreenInternal(false, false);
518 }
519
520 // The variation of showCallScreen() that specifies the initial dialpad state.
521 // (Ideally this would be called showCallScreen() too, just with a different
522 // signature, but AIDL doesn't allow that.)
523 public boolean showCallScreenWithDialpad(boolean showDialpad) {
524 return showCallScreenInternal(true, showDialpad);
525 }
526
527 /**
528 * End a call based on call state
529 * @return true is a call was ended
530 */
531 public boolean endCall() {
532 enforceCallPermission();
533 return (Boolean) sendRequest(CMD_END_CALL, null);
534 }
535
536 public void answerRingingCall() {
537 if (DBG) log("answerRingingCall...");
538 // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
539 // but that can probably wait till the big TelephonyManager API overhaul.
540 // For now, protect this call with the MODIFY_PHONE_STATE permission.
541 enforceModifyPermission();
542 sendRequestAsync(CMD_ANSWER_RINGING_CALL);
543 }
544
545 /**
546 * Make the actual telephony calls to implement answerRingingCall().
547 * This should only be called from the main thread of the Phone app.
548 * @see #answerRingingCall
549 *
550 * TODO: it would be nice to return true if we answered the call, or
551 * false if there wasn't actually a ringing incoming call, or some
552 * other error occurred. (In other words, pass back the return value
553 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
554 * But that would require calling this method via sendRequest() rather
555 * than sendRequestAsync(), and right now we don't actually *need* that
556 * return value, so let's just return void for now.
557 */
558 private void answerRingingCallInternal() {
559 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
560 if (hasRingingCall) {
561 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
562 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
563 if (hasActiveCall && hasHoldingCall) {
564 // Both lines are in use!
565 // TODO: provide a flag to let the caller specify what
566 // policy to use if both lines are in use. (The current
567 // behavior is hardwired to "answer incoming, end ongoing",
568 // which is how the CALL button is specced to behave.)
569 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
570 return;
571 } else {
572 // answerCall() will automatically hold the current active
573 // call, if there is one.
574 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
575 return;
576 }
577 } else {
578 // No call was ringing.
579 return;
580 }
581 }
582
583 public void silenceRinger() {
584 if (DBG) log("silenceRinger...");
585 // TODO: find a more appropriate permission to check here.
586 // (That can probably wait till the big TelephonyManager API overhaul.
587 // For now, protect this call with the MODIFY_PHONE_STATE permission.)
588 enforceModifyPermission();
589 sendRequestAsync(CMD_SILENCE_RINGER);
590 }
591
592 /**
593 * Internal implemenation of silenceRinger().
594 * This should only be called from the main thread of the Phone app.
595 * @see #silenceRinger
596 */
597 private void silenceRingerInternal() {
598 if ((mCM.getState() == PhoneConstants.State.RINGING)
599 && mApp.notifier.isRinging()) {
600 // Ringer is actually playing, so silence it.
601 if (DBG) log("silenceRingerInternal: silencing...");
602 mApp.notifier.silenceRinger();
603 }
604 }
605
606 public boolean isOffhook() {
607 return (mCM.getState() == PhoneConstants.State.OFFHOOK);
608 }
609
610 public boolean isRinging() {
611 return (mCM.getState() == PhoneConstants.State.RINGING);
612 }
613
614 public boolean isIdle() {
615 return (mCM.getState() == PhoneConstants.State.IDLE);
616 }
617
618 public boolean isSimPinEnabled() {
619 enforceReadPermission();
620 return (PhoneGlobals.getInstance().isSimPinEnabled());
621 }
622
623 public boolean supplyPin(String pin) {
Wink Saville9de0f752013-10-22 19:04:03 -0700624 int [] resultArray = supplyPinReportResult(pin);
625 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
626 }
627
628 public boolean supplyPuk(String puk, String pin) {
629 int [] resultArray = supplyPukReportResult(puk, pin);
630 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
631 }
632
633 /** {@hide} */
634 public int[] supplyPinReportResult(String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700635 enforceModifyPermission();
636 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
637 checkSimPin.start();
638 return checkSimPin.unlockSim(null, pin);
639 }
640
Wink Saville9de0f752013-10-22 19:04:03 -0700641 /** {@hide} */
642 public int[] supplyPukReportResult(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700643 enforceModifyPermission();
644 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
645 checkSimPuk.start();
646 return checkSimPuk.unlockSim(puk, pin);
647 }
648
649 /**
Wink Saville9de0f752013-10-22 19:04:03 -0700650 * Helper thread to turn async call to SimCard#supplyPin into
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700651 * a synchronous one.
652 */
653 private static class UnlockSim extends Thread {
654
655 private final IccCard mSimCard;
656
657 private boolean mDone = false;
Wink Saville9de0f752013-10-22 19:04:03 -0700658 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
659 private int mRetryCount = -1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700660
661 // For replies from SimCard interface
662 private Handler mHandler;
663
664 // For async handler to identify request type
665 private static final int SUPPLY_PIN_COMPLETE = 100;
666
667 public UnlockSim(IccCard simCard) {
668 mSimCard = simCard;
669 }
670
671 @Override
672 public void run() {
673 Looper.prepare();
674 synchronized (UnlockSim.this) {
675 mHandler = new Handler() {
676 @Override
677 public void handleMessage(Message msg) {
678 AsyncResult ar = (AsyncResult) msg.obj;
679 switch (msg.what) {
680 case SUPPLY_PIN_COMPLETE:
681 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
682 synchronized (UnlockSim.this) {
Wink Saville9de0f752013-10-22 19:04:03 -0700683 mRetryCount = msg.arg1;
684 if (ar.exception != null) {
685 if (ar.exception instanceof CommandException &&
686 ((CommandException)(ar.exception)).getCommandError()
687 == CommandException.Error.PASSWORD_INCORRECT) {
688 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
689 } else {
690 mResult = PhoneConstants.PIN_GENERAL_FAILURE;
691 }
692 } else {
693 mResult = PhoneConstants.PIN_RESULT_SUCCESS;
694 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700695 mDone = true;
696 UnlockSim.this.notifyAll();
697 }
698 break;
699 }
700 }
701 };
702 UnlockSim.this.notifyAll();
703 }
704 Looper.loop();
705 }
706
707 /*
708 * Use PIN or PUK to unlock SIM card
709 *
710 * If PUK is null, unlock SIM card with PIN
711 *
712 * If PUK is not null, unlock SIM card with PUK and set PIN code
713 */
Wink Saville9de0f752013-10-22 19:04:03 -0700714 synchronized int[] unlockSim(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700715
716 while (mHandler == null) {
717 try {
718 wait();
719 } catch (InterruptedException e) {
720 Thread.currentThread().interrupt();
721 }
722 }
723 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
724
725 if (puk == null) {
726 mSimCard.supplyPin(pin, callback);
727 } else {
728 mSimCard.supplyPuk(puk, pin, callback);
729 }
730
731 while (!mDone) {
732 try {
733 Log.d(LOG_TAG, "wait for done");
734 wait();
735 } catch (InterruptedException e) {
736 // Restore the interrupted status
737 Thread.currentThread().interrupt();
738 }
739 }
740 Log.d(LOG_TAG, "done");
Wink Saville9de0f752013-10-22 19:04:03 -0700741 int[] resultArray = new int[2];
742 resultArray[0] = mResult;
743 resultArray[1] = mRetryCount;
744 return resultArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700745 }
746 }
747
748 public void updateServiceLocation() {
749 // No permission check needed here: this call is harmless, and it's
750 // needed for the ServiceState.requestStateUpdate() call (which is
751 // already intentionally exposed to 3rd parties.)
752 mPhone.updateServiceLocation();
753 }
754
755 public boolean isRadioOn() {
756 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
757 }
758
759 public void toggleRadioOnOff() {
760 enforceModifyPermission();
761 mPhone.setRadioPower(!isRadioOn());
762 }
763 public boolean setRadio(boolean turnOn) {
764 enforceModifyPermission();
765 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
766 toggleRadioOnOff();
767 }
768 return true;
769 }
770 public boolean setRadioPower(boolean turnOn) {
771 enforceModifyPermission();
772 mPhone.setRadioPower(turnOn);
773 return true;
774 }
775
776 public boolean enableDataConnectivity() {
777 enforceModifyPermission();
778 ConnectivityManager cm =
779 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
780 cm.setMobileDataEnabled(true);
781 return true;
782 }
783
784 public int enableApnType(String type) {
785 enforceModifyPermission();
786 return mPhone.enableApnType(type);
787 }
788
789 public int disableApnType(String type) {
790 enforceModifyPermission();
791 return mPhone.disableApnType(type);
792 }
793
794 public boolean disableDataConnectivity() {
795 enforceModifyPermission();
796 ConnectivityManager cm =
797 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
798 cm.setMobileDataEnabled(false);
799 return true;
800 }
801
802 public boolean isDataConnectivityPossible() {
803 return mPhone.isDataConnectivityPossible();
804 }
805
806 public boolean handlePinMmi(String dialString) {
807 enforceModifyPermission();
808 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
809 }
810
811 public void cancelMissedCallsNotification() {
812 enforceModifyPermission();
813 mApp.notificationMgr.cancelMissedCallNotification();
814 }
815
816 public int getCallState() {
817 return DefaultPhoneNotifier.convertCallState(mCM.getState());
818 }
819
820 public int getDataState() {
821 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
822 }
823
824 public int getDataActivity() {
825 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
826 }
827
828 @Override
829 public Bundle getCellLocation() {
830 try {
831 mApp.enforceCallingOrSelfPermission(
832 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
833 } catch (SecurityException e) {
834 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
835 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
836 // is the weaker precondition
837 mApp.enforceCallingOrSelfPermission(
838 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
839 }
840
Jake Hambye994d462014-02-03 13:10:13 -0800841 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700842 if (DBG_LOC) log("getCellLocation: is active user");
843 Bundle data = new Bundle();
844 mPhone.getCellLocation().fillInNotifierBundle(data);
845 return data;
846 } else {
847 if (DBG_LOC) log("getCellLocation: suppress non-active user");
848 return null;
849 }
850 }
851
852 @Override
853 public void enableLocationUpdates() {
854 mApp.enforceCallingOrSelfPermission(
855 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
856 mPhone.enableLocationUpdates();
857 }
858
859 @Override
860 public void disableLocationUpdates() {
861 mApp.enforceCallingOrSelfPermission(
862 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
863 mPhone.disableLocationUpdates();
864 }
865
866 @Override
867 @SuppressWarnings("unchecked")
868 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
869 try {
870 mApp.enforceCallingOrSelfPermission(
871 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
872 } catch (SecurityException e) {
873 // If we have ACCESS_FINE_LOCATION permission, skip the check
874 // for ACCESS_COARSE_LOCATION
875 // A failure should throw the SecurityException from
876 // ACCESS_COARSE_LOCATION since this is the weaker precondition
877 mApp.enforceCallingOrSelfPermission(
878 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
879 }
880
881 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
882 callingPackage) != AppOpsManager.MODE_ALLOWED) {
883 return null;
884 }
Jake Hambye994d462014-02-03 13:10:13 -0800885 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700886 if (DBG_LOC) log("getNeighboringCellInfo: is active user");
887
888 ArrayList<NeighboringCellInfo> cells = null;
889
890 try {
891 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
892 CMD_HANDLE_NEIGHBORING_CELL, null);
893 } catch (RuntimeException e) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700894 loge("getNeighboringCellInfo " + e);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700895 }
896 return cells;
897 } else {
898 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
899 return null;
900 }
901 }
902
903
904 @Override
905 public List<CellInfo> getAllCellInfo() {
906 try {
907 mApp.enforceCallingOrSelfPermission(
908 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
909 } catch (SecurityException e) {
910 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
911 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
912 // is the weaker precondition
913 mApp.enforceCallingOrSelfPermission(
914 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
915 }
916
Jake Hambye994d462014-02-03 13:10:13 -0800917 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700918 if (DBG_LOC) log("getAllCellInfo: is active user");
919 return mPhone.getAllCellInfo();
920 } else {
921 if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
922 return null;
923 }
924 }
925
926 public void setCellInfoListRate(int rateInMillis) {
927 mPhone.setCellInfoListRate(rateInMillis);
928 }
929
930 //
931 // Internal helper methods.
932 //
933
Jake Hambye994d462014-02-03 13:10:13 -0800934 private static boolean checkIfCallerIsSelfOrForegroundUser() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700935 boolean ok;
936
937 boolean self = Binder.getCallingUid() == Process.myUid();
938 if (!self) {
939 // Get the caller's user id then clear the calling identity
940 // which will be restored in the finally clause.
941 int callingUser = UserHandle.getCallingUserId();
942 long ident = Binder.clearCallingIdentity();
943
944 try {
945 // With calling identity cleared the current user is the foreground user.
946 int foregroundUser = ActivityManager.getCurrentUser();
947 ok = (foregroundUser == callingUser);
948 if (DBG_LOC) {
949 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
950 + " callingUser=" + callingUser + " ok=" + ok);
951 }
952 } catch (Exception ex) {
953 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
954 ok = false;
955 } finally {
956 Binder.restoreCallingIdentity(ident);
957 }
958 } else {
959 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
960 ok = true;
961 }
962 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
963 return ok;
964 }
965
966 /**
967 * Make sure the caller has the READ_PHONE_STATE permission.
968 *
969 * @throws SecurityException if the caller does not have the required permission
970 */
971 private void enforceReadPermission() {
972 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
973 }
974
975 /**
976 * Make sure the caller has the MODIFY_PHONE_STATE permission.
977 *
978 * @throws SecurityException if the caller does not have the required permission
979 */
980 private void enforceModifyPermission() {
981 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
982 }
983
984 /**
985 * Make sure the caller has the CALL_PHONE permission.
986 *
987 * @throws SecurityException if the caller does not have the required permission
988 */
989 private void enforceCallPermission() {
990 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
991 }
992
Shishir Agrawal566b7612013-10-28 14:41:00 -0700993 /**
994 * Make sure the caller has SIM_COMMUNICATION permission.
995 *
996 * @throws SecurityException if the caller does not have the required permission.
997 */
998 private void enforceSimCommunicationPermission() {
999 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.SIM_COMMUNICATION, null);
1000 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001001
1002 private String createTelUrl(String number) {
1003 if (TextUtils.isEmpty(number)) {
1004 return null;
1005 }
1006
Jake Hambye994d462014-02-03 13:10:13 -08001007 return "tel:" + number;
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001008 }
1009
Jake Hambye994d462014-02-03 13:10:13 -08001010 private static void log(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001011 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
1012 }
1013
Jake Hambye994d462014-02-03 13:10:13 -08001014 private static void loge(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001015 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
1016 }
1017
1018 public int getActivePhoneType() {
1019 return mPhone.getPhoneType();
1020 }
1021
1022 /**
1023 * Returns the CDMA ERI icon index to display
1024 */
1025 public int getCdmaEriIconIndex() {
1026 return mPhone.getCdmaEriIconIndex();
1027 }
1028
1029 /**
1030 * Returns the CDMA ERI icon mode,
1031 * 0 - ON
1032 * 1 - FLASHING
1033 */
1034 public int getCdmaEriIconMode() {
1035 return mPhone.getCdmaEriIconMode();
1036 }
1037
1038 /**
1039 * Returns the CDMA ERI text,
1040 */
1041 public String getCdmaEriText() {
1042 return mPhone.getCdmaEriText();
1043 }
1044
1045 /**
1046 * Returns true if CDMA provisioning needs to run.
1047 */
1048 public boolean needsOtaServiceProvisioning() {
1049 return mPhone.needsOtaServiceProvisioning();
1050 }
1051
1052 /**
1053 * Returns the unread count of voicemails
1054 */
1055 public int getVoiceMessageCount() {
1056 return mPhone.getVoiceMessageCount();
1057 }
1058
1059 /**
1060 * Returns the data network type
1061 *
1062 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
1063 */
1064 @Override
1065 public int getNetworkType() {
1066 return mPhone.getServiceState().getDataNetworkType();
1067 }
1068
1069 /**
1070 * Returns the data network type
1071 */
1072 @Override
1073 public int getDataNetworkType() {
1074 return mPhone.getServiceState().getDataNetworkType();
1075 }
1076
1077 /**
1078 * Returns the data network type
1079 */
1080 @Override
1081 public int getVoiceNetworkType() {
1082 return mPhone.getServiceState().getVoiceNetworkType();
1083 }
1084
1085 /**
1086 * @return true if a ICC card is present
1087 */
1088 public boolean hasIccCard() {
1089 return mPhone.getIccCard().hasIccCard();
1090 }
1091
1092 /**
1093 * Return if the current radio is LTE on CDMA. This
1094 * is a tri-state return value as for a period of time
1095 * the mode may be unknown.
1096 *
1097 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
Jake Hambye994d462014-02-03 13:10:13 -08001098 * or {@link Phone#LTE_ON_CDMA_TRUE}
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001099 */
1100 public int getLteOnCdmaMode() {
1101 return mPhone.getLteOnCdmaMode();
1102 }
Shishir Agrawal566b7612013-10-28 14:41:00 -07001103
1104 @Override
1105 public int iccOpenLogicalChannel(String AID) {
1106 enforceSimCommunicationPermission();
1107
1108 if (DBG) log("iccOpenLogicalChannel: " + AID);
1109 Integer channel = (Integer)sendRequest(CMD_OPEN_CHANNEL, AID);
1110 if (DBG) log("iccOpenLogicalChannel: " + channel);
Jake Hambye994d462014-02-03 13:10:13 -08001111 return channel;
Shishir Agrawal566b7612013-10-28 14:41:00 -07001112 }
1113
1114 @Override
1115 public boolean iccCloseLogicalChannel(int channel) {
1116 enforceSimCommunicationPermission();
1117
1118 if (DBG) log("iccCloseLogicalChannel: " + channel);
1119 if (channel < 0) {
1120 return false;
1121 }
Jake Hambye994d462014-02-03 13:10:13 -08001122 Boolean success = (Boolean)sendRequest(CMD_CLOSE_CHANNEL, channel);
Shishir Agrawal566b7612013-10-28 14:41:00 -07001123 if (DBG) log("iccCloseLogicalChannel: " + success);
1124 return success;
1125 }
1126
1127 @Override
1128 public String iccTransmitApduLogicalChannel(int channel, int cla,
1129 int command, int p1, int p2, int p3, String data) {
1130 enforceSimCommunicationPermission();
1131
1132 if (DBG) {
1133 log("iccTransmitApduLogicalChannel: chnl=" + channel + " cla=" + cla +
1134 " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 +
1135 " data=" + data);
1136 }
1137
1138 if (channel < 0) {
1139 return "";
1140 }
1141
1142 IccIoResult response = (IccIoResult)sendRequest(CMD_TRANSMIT_APDU,
1143 new IccAPDUArgument(channel, cla, command, p1, p2, p3, data));
1144 if (DBG) log("iccTransmitApduLogicalChannel: " + response);
1145
1146 // If the payload is null, there was an error. Indicate that by returning
1147 // an empty string.
1148 if (response.payload == null) {
1149 return "";
1150 }
1151
1152 // Append the returned status code to the end of the response payload.
1153 String s = Integer.toHexString(
1154 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1155 s = IccUtils.bytesToHexString(response.payload) + s;
1156 return s;
1157 }
Jake Hambye994d462014-02-03 13:10:13 -08001158
1159 /**
1160 * Read one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1161 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1162 *
1163 * @param itemID the ID of the item to read
1164 * @return the NV item as a String, or null on error.
1165 */
1166 @Override
1167 public String nvReadItem(int itemID) {
1168 enforceModifyPermission();
1169 if (DBG) log("nvReadItem: item " + itemID);
1170 String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID);
1171 if (DBG) log("nvReadItem: item " + itemID + " is \"" + value + '"');
1172 return value;
1173 }
1174
1175 /**
1176 * Write one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1177 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1178 *
1179 * @param itemID the ID of the item to read
1180 * @param itemValue the value to write, as a String
1181 * @return true on success; false on any failure
1182 */
1183 @Override
1184 public boolean nvWriteItem(int itemID, String itemValue) {
1185 enforceModifyPermission();
1186 if (DBG) log("nvWriteItem: item " + itemID + " value \"" + itemValue + '"');
1187 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_ITEM,
1188 new Pair<Integer, String>(itemID, itemValue));
1189 if (DBG) log("nvWriteItem: item " + itemID + ' ' + (success ? "ok" : "fail"));
1190 return success;
1191 }
1192
1193 /**
1194 * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
1195 * Used for device configuration by some CDMA operators.
1196 *
1197 * @param preferredRoamingList byte array containing the new PRL
1198 * @return true on success; false on any failure
1199 */
1200 @Override
1201 public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
1202 enforceModifyPermission();
1203 if (DBG) log("nvWriteCdmaPrl: value: " + HexDump.toHexString(preferredRoamingList));
1204 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_CDMA_PRL, preferredRoamingList);
1205 if (DBG) log("nvWriteCdmaPrl: " + (success ? "ok" : "fail"));
1206 return success;
1207 }
1208
1209 /**
1210 * Perform the specified type of NV config reset.
1211 * Used for device configuration by some CDMA operators.
1212 *
1213 * @param resetType the type of reset to perform (1 == factory reset; 2 == NV-only reset)
1214 * @return true on success; false on any failure
1215 */
1216 @Override
1217 public boolean nvResetConfig(int resetType) {
1218 enforceModifyPermission();
1219 if (DBG) log("nvResetConfig: type " + resetType);
1220 Boolean success = (Boolean) sendRequest(CMD_NV_RESET_CONFIG, resetType);
1221 if (DBG) log("nvResetConfig: type " + resetType + ' ' + (success ? "ok" : "fail"));
1222 return success;
1223 }
1224
1225 /**
1226 * Change the radio to the specified mode.
1227 * Used for device configuration by some operators.
1228 *
1229 * @param radioMode is 0 for offline mode, 1 for online mode, 2 for low-power mode,
1230 * or 3 to reset the radio.
1231 * @return true on success; false on any failure
1232 */
1233 @Override
1234 public boolean setRadioMode(int radioMode) {
1235 enforceModifyPermission();
1236 if (DBG) log("setRadioMode: mode " + radioMode);
1237 Boolean success = (Boolean) sendRequest(CMD_SET_RADIO_MODE, radioMode);
1238 if (DBG) log("setRadioMode: mode " + radioMode + ' ' + (success ? "ok" : "fail"));
1239 return success;
1240 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001241}