blob: 34ed83209a158861f8e03569f178b1966c3db825 [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
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700344 default:
345 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
346 break;
347 }
348 }
Jake Hambye994d462014-02-03 13:10:13 -0800349
350 private void handleNullReturnEvent(Message msg, String command) {
351 AsyncResult ar = (AsyncResult) msg.obj;
352 MainThreadRequest request = (MainThreadRequest) ar.userObj;
353 if (ar.exception == null) {
354 request.result = true;
355 } else {
356 request.result = false;
357 if (ar.exception instanceof CommandException) {
358 loge(command + ": CommandException: " + ar.exception);
359 } else {
360 loge(command + ": Unknown exception");
361 }
362 }
363 synchronized (request) {
364 request.notifyAll();
365 }
366 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700367 }
368
369 /**
370 * Posts the specified command to be executed on the main thread,
371 * waits for the request to complete, and returns the result.
372 * @see #sendRequestAsync
373 */
374 private Object sendRequest(int command, Object argument) {
375 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
376 throw new RuntimeException("This method will deadlock if called from the main thread.");
377 }
378
379 MainThreadRequest request = new MainThreadRequest(argument);
380 Message msg = mMainThreadHandler.obtainMessage(command, request);
381 msg.sendToTarget();
382
383 // Wait for the request to complete
384 synchronized (request) {
385 while (request.result == null) {
386 try {
387 request.wait();
388 } catch (InterruptedException e) {
389 // Do nothing, go back and wait until the request is complete
390 }
391 }
392 }
393 return request.result;
394 }
395
396 /**
397 * Asynchronous ("fire and forget") version of sendRequest():
398 * Posts the specified command to be executed on the main thread, and
399 * returns immediately.
400 * @see #sendRequest
401 */
402 private void sendRequestAsync(int command) {
403 mMainThreadHandler.sendEmptyMessage(command);
404 }
405
406 /**
407 * Initialize the singleton PhoneInterfaceManager instance.
408 * This is only done once, at startup, from PhoneApp.onCreate().
409 */
Santos Cordon406c0342013-08-28 00:07:47 -0700410 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
411 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700412 synchronized (PhoneInterfaceManager.class) {
413 if (sInstance == null) {
Santos Cordon406c0342013-08-28 00:07:47 -0700414 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700415 } else {
416 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
417 }
418 return sInstance;
419 }
420 }
421
422 /** Private constructor; @see init() */
Santos Cordon406c0342013-08-28 00:07:47 -0700423 private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
424 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700425 mApp = app;
426 mPhone = phone;
427 mCM = PhoneGlobals.getInstance().mCM;
428 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
429 mMainThreadHandler = new MainThreadHandler();
Santos Cordon406c0342013-08-28 00:07:47 -0700430 mCallHandlerService = callHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700431 publish();
432 }
433
434 private void publish() {
435 if (DBG) log("publish: " + this);
436
437 ServiceManager.addService("phone", this);
438 }
439
440 //
441 // Implementation of the ITelephony interface.
442 //
443
444 public void dial(String number) {
445 if (DBG) log("dial: " + number);
446 // No permission check needed here: This is just a wrapper around the
447 // ACTION_DIAL intent, which is available to any app since it puts up
448 // the UI before it does anything.
449
450 String url = createTelUrl(number);
451 if (url == null) {
452 return;
453 }
454
455 // PENDING: should we just silently fail if phone is offhook or ringing?
456 PhoneConstants.State state = mCM.getState();
457 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
458 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
459 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
460 mApp.startActivity(intent);
461 }
462 }
463
464 public void call(String callingPackage, String number) {
465 if (DBG) log("call: " + number);
466
467 // This is just a wrapper around the ACTION_CALL intent, but we still
468 // need to do a permission check since we're calling startActivity()
469 // from the context of the phone app.
470 enforceCallPermission();
471
472 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
473 != AppOpsManager.MODE_ALLOWED) {
474 return;
475 }
476
477 String url = createTelUrl(number);
478 if (url == null) {
479 return;
480 }
481
482 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
483 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
484 mApp.startActivity(intent);
485 }
486
487 private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
Makoto Onukibcf20992013-09-12 17:59:30 -0700488 boolean showDialpad) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700489 if (!PhoneGlobals.sVoiceCapable) {
490 // Never allow the InCallScreen to appear on data-only devices.
491 return false;
492 }
493 if (isIdle()) {
494 return false;
495 }
496 // If the phone isn't idle then go to the in-call screen
497 long callingId = Binder.clearCallingIdentity();
Santos Cordon406c0342013-08-28 00:07:47 -0700498
Makoto Onukibcf20992013-09-12 17:59:30 -0700499 mCallHandlerService.bringToForeground(showDialpad);
Santos Cordon406c0342013-08-28 00:07:47 -0700500
501 Binder.restoreCallingIdentity(callingId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700502 return true;
503 }
504
505 // Show the in-call screen without specifying the initial dialpad state.
506 public boolean showCallScreen() {
507 return showCallScreenInternal(false, false);
508 }
509
510 // The variation of showCallScreen() that specifies the initial dialpad state.
511 // (Ideally this would be called showCallScreen() too, just with a different
512 // signature, but AIDL doesn't allow that.)
513 public boolean showCallScreenWithDialpad(boolean showDialpad) {
514 return showCallScreenInternal(true, showDialpad);
515 }
516
517 /**
518 * End a call based on call state
519 * @return true is a call was ended
520 */
521 public boolean endCall() {
522 enforceCallPermission();
523 return (Boolean) sendRequest(CMD_END_CALL, null);
524 }
525
526 public void answerRingingCall() {
527 if (DBG) log("answerRingingCall...");
528 // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
529 // but that can probably wait till the big TelephonyManager API overhaul.
530 // For now, protect this call with the MODIFY_PHONE_STATE permission.
531 enforceModifyPermission();
532 sendRequestAsync(CMD_ANSWER_RINGING_CALL);
533 }
534
535 /**
536 * Make the actual telephony calls to implement answerRingingCall().
537 * This should only be called from the main thread of the Phone app.
538 * @see #answerRingingCall
539 *
540 * TODO: it would be nice to return true if we answered the call, or
541 * false if there wasn't actually a ringing incoming call, or some
542 * other error occurred. (In other words, pass back the return value
543 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
544 * But that would require calling this method via sendRequest() rather
545 * than sendRequestAsync(), and right now we don't actually *need* that
546 * return value, so let's just return void for now.
547 */
548 private void answerRingingCallInternal() {
549 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
550 if (hasRingingCall) {
551 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
552 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
553 if (hasActiveCall && hasHoldingCall) {
554 // Both lines are in use!
555 // TODO: provide a flag to let the caller specify what
556 // policy to use if both lines are in use. (The current
557 // behavior is hardwired to "answer incoming, end ongoing",
558 // which is how the CALL button is specced to behave.)
559 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
560 return;
561 } else {
562 // answerCall() will automatically hold the current active
563 // call, if there is one.
564 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
565 return;
566 }
567 } else {
568 // No call was ringing.
569 return;
570 }
571 }
572
573 public void silenceRinger() {
574 if (DBG) log("silenceRinger...");
575 // TODO: find a more appropriate permission to check here.
576 // (That can probably wait till the big TelephonyManager API overhaul.
577 // For now, protect this call with the MODIFY_PHONE_STATE permission.)
578 enforceModifyPermission();
579 sendRequestAsync(CMD_SILENCE_RINGER);
580 }
581
582 /**
583 * Internal implemenation of silenceRinger().
584 * This should only be called from the main thread of the Phone app.
585 * @see #silenceRinger
586 */
587 private void silenceRingerInternal() {
588 if ((mCM.getState() == PhoneConstants.State.RINGING)
589 && mApp.notifier.isRinging()) {
590 // Ringer is actually playing, so silence it.
591 if (DBG) log("silenceRingerInternal: silencing...");
592 mApp.notifier.silenceRinger();
593 }
594 }
595
596 public boolean isOffhook() {
597 return (mCM.getState() == PhoneConstants.State.OFFHOOK);
598 }
599
600 public boolean isRinging() {
601 return (mCM.getState() == PhoneConstants.State.RINGING);
602 }
603
604 public boolean isIdle() {
605 return (mCM.getState() == PhoneConstants.State.IDLE);
606 }
607
608 public boolean isSimPinEnabled() {
609 enforceReadPermission();
610 return (PhoneGlobals.getInstance().isSimPinEnabled());
611 }
612
613 public boolean supplyPin(String pin) {
Wink Saville9de0f752013-10-22 19:04:03 -0700614 int [] resultArray = supplyPinReportResult(pin);
615 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
616 }
617
618 public boolean supplyPuk(String puk, String pin) {
619 int [] resultArray = supplyPukReportResult(puk, pin);
620 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
621 }
622
623 /** {@hide} */
624 public int[] supplyPinReportResult(String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700625 enforceModifyPermission();
626 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
627 checkSimPin.start();
628 return checkSimPin.unlockSim(null, pin);
629 }
630
Wink Saville9de0f752013-10-22 19:04:03 -0700631 /** {@hide} */
632 public int[] supplyPukReportResult(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700633 enforceModifyPermission();
634 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
635 checkSimPuk.start();
636 return checkSimPuk.unlockSim(puk, pin);
637 }
638
639 /**
Wink Saville9de0f752013-10-22 19:04:03 -0700640 * Helper thread to turn async call to SimCard#supplyPin into
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700641 * a synchronous one.
642 */
643 private static class UnlockSim extends Thread {
644
645 private final IccCard mSimCard;
646
647 private boolean mDone = false;
Wink Saville9de0f752013-10-22 19:04:03 -0700648 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
649 private int mRetryCount = -1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700650
651 // For replies from SimCard interface
652 private Handler mHandler;
653
654 // For async handler to identify request type
655 private static final int SUPPLY_PIN_COMPLETE = 100;
656
657 public UnlockSim(IccCard simCard) {
658 mSimCard = simCard;
659 }
660
661 @Override
662 public void run() {
663 Looper.prepare();
664 synchronized (UnlockSim.this) {
665 mHandler = new Handler() {
666 @Override
667 public void handleMessage(Message msg) {
668 AsyncResult ar = (AsyncResult) msg.obj;
669 switch (msg.what) {
670 case SUPPLY_PIN_COMPLETE:
671 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
672 synchronized (UnlockSim.this) {
Wink Saville9de0f752013-10-22 19:04:03 -0700673 mRetryCount = msg.arg1;
674 if (ar.exception != null) {
675 if (ar.exception instanceof CommandException &&
676 ((CommandException)(ar.exception)).getCommandError()
677 == CommandException.Error.PASSWORD_INCORRECT) {
678 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
679 } else {
680 mResult = PhoneConstants.PIN_GENERAL_FAILURE;
681 }
682 } else {
683 mResult = PhoneConstants.PIN_RESULT_SUCCESS;
684 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700685 mDone = true;
686 UnlockSim.this.notifyAll();
687 }
688 break;
689 }
690 }
691 };
692 UnlockSim.this.notifyAll();
693 }
694 Looper.loop();
695 }
696
697 /*
698 * Use PIN or PUK to unlock SIM card
699 *
700 * If PUK is null, unlock SIM card with PIN
701 *
702 * If PUK is not null, unlock SIM card with PUK and set PIN code
703 */
Wink Saville9de0f752013-10-22 19:04:03 -0700704 synchronized int[] unlockSim(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700705
706 while (mHandler == null) {
707 try {
708 wait();
709 } catch (InterruptedException e) {
710 Thread.currentThread().interrupt();
711 }
712 }
713 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
714
715 if (puk == null) {
716 mSimCard.supplyPin(pin, callback);
717 } else {
718 mSimCard.supplyPuk(puk, pin, callback);
719 }
720
721 while (!mDone) {
722 try {
723 Log.d(LOG_TAG, "wait for done");
724 wait();
725 } catch (InterruptedException e) {
726 // Restore the interrupted status
727 Thread.currentThread().interrupt();
728 }
729 }
730 Log.d(LOG_TAG, "done");
Wink Saville9de0f752013-10-22 19:04:03 -0700731 int[] resultArray = new int[2];
732 resultArray[0] = mResult;
733 resultArray[1] = mRetryCount;
734 return resultArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700735 }
736 }
737
738 public void updateServiceLocation() {
739 // No permission check needed here: this call is harmless, and it's
740 // needed for the ServiceState.requestStateUpdate() call (which is
741 // already intentionally exposed to 3rd parties.)
742 mPhone.updateServiceLocation();
743 }
744
745 public boolean isRadioOn() {
746 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
747 }
748
749 public void toggleRadioOnOff() {
750 enforceModifyPermission();
751 mPhone.setRadioPower(!isRadioOn());
752 }
753 public boolean setRadio(boolean turnOn) {
754 enforceModifyPermission();
755 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
756 toggleRadioOnOff();
757 }
758 return true;
759 }
760 public boolean setRadioPower(boolean turnOn) {
761 enforceModifyPermission();
762 mPhone.setRadioPower(turnOn);
763 return true;
764 }
765
766 public boolean enableDataConnectivity() {
767 enforceModifyPermission();
768 ConnectivityManager cm =
769 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
770 cm.setMobileDataEnabled(true);
771 return true;
772 }
773
774 public int enableApnType(String type) {
775 enforceModifyPermission();
776 return mPhone.enableApnType(type);
777 }
778
779 public int disableApnType(String type) {
780 enforceModifyPermission();
781 return mPhone.disableApnType(type);
782 }
783
784 public boolean disableDataConnectivity() {
785 enforceModifyPermission();
786 ConnectivityManager cm =
787 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
788 cm.setMobileDataEnabled(false);
789 return true;
790 }
791
792 public boolean isDataConnectivityPossible() {
793 return mPhone.isDataConnectivityPossible();
794 }
795
796 public boolean handlePinMmi(String dialString) {
797 enforceModifyPermission();
798 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
799 }
800
801 public void cancelMissedCallsNotification() {
802 enforceModifyPermission();
803 mApp.notificationMgr.cancelMissedCallNotification();
804 }
805
806 public int getCallState() {
807 return DefaultPhoneNotifier.convertCallState(mCM.getState());
808 }
809
810 public int getDataState() {
811 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
812 }
813
814 public int getDataActivity() {
815 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
816 }
817
818 @Override
819 public Bundle getCellLocation() {
820 try {
821 mApp.enforceCallingOrSelfPermission(
822 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
823 } catch (SecurityException e) {
824 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
825 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
826 // is the weaker precondition
827 mApp.enforceCallingOrSelfPermission(
828 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
829 }
830
Jake Hambye994d462014-02-03 13:10:13 -0800831 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700832 if (DBG_LOC) log("getCellLocation: is active user");
833 Bundle data = new Bundle();
834 mPhone.getCellLocation().fillInNotifierBundle(data);
835 return data;
836 } else {
837 if (DBG_LOC) log("getCellLocation: suppress non-active user");
838 return null;
839 }
840 }
841
842 @Override
843 public void enableLocationUpdates() {
844 mApp.enforceCallingOrSelfPermission(
845 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
846 mPhone.enableLocationUpdates();
847 }
848
849 @Override
850 public void disableLocationUpdates() {
851 mApp.enforceCallingOrSelfPermission(
852 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
853 mPhone.disableLocationUpdates();
854 }
855
856 @Override
857 @SuppressWarnings("unchecked")
858 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
859 try {
860 mApp.enforceCallingOrSelfPermission(
861 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
862 } catch (SecurityException e) {
863 // If we have ACCESS_FINE_LOCATION permission, skip the check
864 // for ACCESS_COARSE_LOCATION
865 // A failure should throw the SecurityException from
866 // ACCESS_COARSE_LOCATION since this is the weaker precondition
867 mApp.enforceCallingOrSelfPermission(
868 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
869 }
870
871 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
872 callingPackage) != AppOpsManager.MODE_ALLOWED) {
873 return null;
874 }
Jake Hambye994d462014-02-03 13:10:13 -0800875 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700876 if (DBG_LOC) log("getNeighboringCellInfo: is active user");
877
878 ArrayList<NeighboringCellInfo> cells = null;
879
880 try {
881 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
882 CMD_HANDLE_NEIGHBORING_CELL, null);
883 } catch (RuntimeException e) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700884 loge("getNeighboringCellInfo " + e);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700885 }
886 return cells;
887 } else {
888 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
889 return null;
890 }
891 }
892
893
894 @Override
895 public List<CellInfo> getAllCellInfo() {
896 try {
897 mApp.enforceCallingOrSelfPermission(
898 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
899 } catch (SecurityException e) {
900 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
901 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
902 // is the weaker precondition
903 mApp.enforceCallingOrSelfPermission(
904 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
905 }
906
Jake Hambye994d462014-02-03 13:10:13 -0800907 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700908 if (DBG_LOC) log("getAllCellInfo: is active user");
909 return mPhone.getAllCellInfo();
910 } else {
911 if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
912 return null;
913 }
914 }
915
916 public void setCellInfoListRate(int rateInMillis) {
917 mPhone.setCellInfoListRate(rateInMillis);
918 }
919
920 //
921 // Internal helper methods.
922 //
923
Jake Hambye994d462014-02-03 13:10:13 -0800924 private static boolean checkIfCallerIsSelfOrForegroundUser() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700925 boolean ok;
926
927 boolean self = Binder.getCallingUid() == Process.myUid();
928 if (!self) {
929 // Get the caller's user id then clear the calling identity
930 // which will be restored in the finally clause.
931 int callingUser = UserHandle.getCallingUserId();
932 long ident = Binder.clearCallingIdentity();
933
934 try {
935 // With calling identity cleared the current user is the foreground user.
936 int foregroundUser = ActivityManager.getCurrentUser();
937 ok = (foregroundUser == callingUser);
938 if (DBG_LOC) {
939 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
940 + " callingUser=" + callingUser + " ok=" + ok);
941 }
942 } catch (Exception ex) {
943 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
944 ok = false;
945 } finally {
946 Binder.restoreCallingIdentity(ident);
947 }
948 } else {
949 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
950 ok = true;
951 }
952 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
953 return ok;
954 }
955
956 /**
957 * Make sure the caller has the READ_PHONE_STATE permission.
958 *
959 * @throws SecurityException if the caller does not have the required permission
960 */
961 private void enforceReadPermission() {
962 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
963 }
964
965 /**
966 * Make sure the caller has the MODIFY_PHONE_STATE permission.
967 *
968 * @throws SecurityException if the caller does not have the required permission
969 */
970 private void enforceModifyPermission() {
971 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
972 }
973
974 /**
975 * Make sure the caller has the CALL_PHONE permission.
976 *
977 * @throws SecurityException if the caller does not have the required permission
978 */
979 private void enforceCallPermission() {
980 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
981 }
982
Shishir Agrawal566b7612013-10-28 14:41:00 -0700983 /**
984 * Make sure the caller has SIM_COMMUNICATION permission.
985 *
986 * @throws SecurityException if the caller does not have the required permission.
987 */
988 private void enforceSimCommunicationPermission() {
989 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.SIM_COMMUNICATION, null);
990 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700991
992 private String createTelUrl(String number) {
993 if (TextUtils.isEmpty(number)) {
994 return null;
995 }
996
Jake Hambye994d462014-02-03 13:10:13 -0800997 return "tel:" + number;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700998 }
999
Jake Hambye994d462014-02-03 13:10:13 -08001000 private static void log(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001001 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
1002 }
1003
Jake Hambye994d462014-02-03 13:10:13 -08001004 private static void loge(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001005 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
1006 }
1007
1008 public int getActivePhoneType() {
1009 return mPhone.getPhoneType();
1010 }
1011
1012 /**
1013 * Returns the CDMA ERI icon index to display
1014 */
1015 public int getCdmaEriIconIndex() {
1016 return mPhone.getCdmaEriIconIndex();
1017 }
1018
1019 /**
1020 * Returns the CDMA ERI icon mode,
1021 * 0 - ON
1022 * 1 - FLASHING
1023 */
1024 public int getCdmaEriIconMode() {
1025 return mPhone.getCdmaEriIconMode();
1026 }
1027
1028 /**
1029 * Returns the CDMA ERI text,
1030 */
1031 public String getCdmaEriText() {
1032 return mPhone.getCdmaEriText();
1033 }
1034
1035 /**
1036 * Returns true if CDMA provisioning needs to run.
1037 */
1038 public boolean needsOtaServiceProvisioning() {
1039 return mPhone.needsOtaServiceProvisioning();
1040 }
1041
1042 /**
1043 * Returns the unread count of voicemails
1044 */
1045 public int getVoiceMessageCount() {
1046 return mPhone.getVoiceMessageCount();
1047 }
1048
1049 /**
1050 * Returns the data network type
1051 *
1052 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
1053 */
1054 @Override
1055 public int getNetworkType() {
1056 return mPhone.getServiceState().getDataNetworkType();
1057 }
1058
1059 /**
1060 * Returns the data network type
1061 */
1062 @Override
1063 public int getDataNetworkType() {
1064 return mPhone.getServiceState().getDataNetworkType();
1065 }
1066
1067 /**
1068 * Returns the data network type
1069 */
1070 @Override
1071 public int getVoiceNetworkType() {
1072 return mPhone.getServiceState().getVoiceNetworkType();
1073 }
1074
1075 /**
1076 * @return true if a ICC card is present
1077 */
1078 public boolean hasIccCard() {
1079 return mPhone.getIccCard().hasIccCard();
1080 }
1081
1082 /**
1083 * Return if the current radio is LTE on CDMA. This
1084 * is a tri-state return value as for a period of time
1085 * the mode may be unknown.
1086 *
1087 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
Jake Hambye994d462014-02-03 13:10:13 -08001088 * or {@link Phone#LTE_ON_CDMA_TRUE}
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001089 */
1090 public int getLteOnCdmaMode() {
1091 return mPhone.getLteOnCdmaMode();
1092 }
Shishir Agrawal566b7612013-10-28 14:41:00 -07001093
1094 @Override
1095 public int iccOpenLogicalChannel(String AID) {
1096 enforceSimCommunicationPermission();
1097
1098 if (DBG) log("iccOpenLogicalChannel: " + AID);
1099 Integer channel = (Integer)sendRequest(CMD_OPEN_CHANNEL, AID);
1100 if (DBG) log("iccOpenLogicalChannel: " + channel);
Jake Hambye994d462014-02-03 13:10:13 -08001101 return channel;
Shishir Agrawal566b7612013-10-28 14:41:00 -07001102 }
1103
1104 @Override
1105 public boolean iccCloseLogicalChannel(int channel) {
1106 enforceSimCommunicationPermission();
1107
1108 if (DBG) log("iccCloseLogicalChannel: " + channel);
1109 if (channel < 0) {
1110 return false;
1111 }
Jake Hambye994d462014-02-03 13:10:13 -08001112 Boolean success = (Boolean)sendRequest(CMD_CLOSE_CHANNEL, channel);
Shishir Agrawal566b7612013-10-28 14:41:00 -07001113 if (DBG) log("iccCloseLogicalChannel: " + success);
1114 return success;
1115 }
1116
1117 @Override
1118 public String iccTransmitApduLogicalChannel(int channel, int cla,
1119 int command, int p1, int p2, int p3, String data) {
1120 enforceSimCommunicationPermission();
1121
1122 if (DBG) {
1123 log("iccTransmitApduLogicalChannel: chnl=" + channel + " cla=" + cla +
1124 " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 +
1125 " data=" + data);
1126 }
1127
1128 if (channel < 0) {
1129 return "";
1130 }
1131
1132 IccIoResult response = (IccIoResult)sendRequest(CMD_TRANSMIT_APDU,
1133 new IccAPDUArgument(channel, cla, command, p1, p2, p3, data));
1134 if (DBG) log("iccTransmitApduLogicalChannel: " + response);
1135
1136 // If the payload is null, there was an error. Indicate that by returning
1137 // an empty string.
1138 if (response.payload == null) {
1139 return "";
1140 }
1141
1142 // Append the returned status code to the end of the response payload.
1143 String s = Integer.toHexString(
1144 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1145 s = IccUtils.bytesToHexString(response.payload) + s;
1146 return s;
1147 }
Jake Hambye994d462014-02-03 13:10:13 -08001148
1149 /**
1150 * Read one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1151 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1152 *
1153 * @param itemID the ID of the item to read
1154 * @return the NV item as a String, or null on error.
1155 */
1156 @Override
1157 public String nvReadItem(int itemID) {
1158 enforceModifyPermission();
1159 if (DBG) log("nvReadItem: item " + itemID);
1160 String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID);
1161 if (DBG) log("nvReadItem: item " + itemID + " is \"" + value + '"');
1162 return value;
1163 }
1164
1165 /**
1166 * Write one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1167 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1168 *
1169 * @param itemID the ID of the item to read
1170 * @param itemValue the value to write, as a String
1171 * @return true on success; false on any failure
1172 */
1173 @Override
1174 public boolean nvWriteItem(int itemID, String itemValue) {
1175 enforceModifyPermission();
1176 if (DBG) log("nvWriteItem: item " + itemID + " value \"" + itemValue + '"');
1177 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_ITEM,
1178 new Pair<Integer, String>(itemID, itemValue));
1179 if (DBG) log("nvWriteItem: item " + itemID + ' ' + (success ? "ok" : "fail"));
1180 return success;
1181 }
1182
1183 /**
1184 * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
1185 * Used for device configuration by some CDMA operators.
1186 *
1187 * @param preferredRoamingList byte array containing the new PRL
1188 * @return true on success; false on any failure
1189 */
1190 @Override
1191 public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
1192 enforceModifyPermission();
1193 if (DBG) log("nvWriteCdmaPrl: value: " + HexDump.toHexString(preferredRoamingList));
1194 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_CDMA_PRL, preferredRoamingList);
1195 if (DBG) log("nvWriteCdmaPrl: " + (success ? "ok" : "fail"));
1196 return success;
1197 }
1198
1199 /**
1200 * Perform the specified type of NV config reset.
1201 * Used for device configuration by some CDMA operators.
1202 *
1203 * @param resetType the type of reset to perform (1 == factory reset; 2 == NV-only reset)
1204 * @return true on success; false on any failure
1205 */
1206 @Override
1207 public boolean nvResetConfig(int resetType) {
1208 enforceModifyPermission();
1209 if (DBG) log("nvResetConfig: type " + resetType);
1210 Boolean success = (Boolean) sendRequest(CMD_NV_RESET_CONFIG, resetType);
1211 if (DBG) log("nvResetConfig: type " + resetType + ' ' + (success ? "ok" : "fail"));
1212 return success;
1213 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001214}