blob: 338c6400457dc3e777103757c62267204d60601d [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;
21import android.content.ActivityNotFoundException;
22import android.content.Context;
23import android.content.Intent;
24import android.net.ConnectivityManager;
25import android.net.Uri;
26import android.os.AsyncResult;
27import android.os.Binder;
28import android.os.Bundle;
29import android.os.Handler;
30import android.os.Looper;
31import android.os.Message;
32import android.os.Process;
33import android.os.ServiceManager;
34import android.os.UserHandle;
35import android.telephony.NeighboringCellInfo;
36import android.telephony.CellInfo;
37import android.telephony.ServiceState;
38import android.text.TextUtils;
39import android.util.Log;
40
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;
44import com.android.internal.telephony.IccCard;
45import com.android.internal.telephony.ITelephony;
46import 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;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070051
52import java.util.List;
53import java.util.ArrayList;
54
55/**
56 * Implementation of the ITelephony interface.
57 */
58public class PhoneInterfaceManager extends ITelephony.Stub {
59 private static final String LOG_TAG = "PhoneInterfaceManager";
60 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
61 private static final boolean DBG_LOC = false;
62
63 // Message codes used with mMainThreadHandler
64 private static final int CMD_HANDLE_PIN_MMI = 1;
65 private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
66 private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
67 private static final int CMD_ANSWER_RINGING_CALL = 4;
68 private static final int CMD_END_CALL = 5; // not used yet
69 private static final int CMD_SILENCE_RINGER = 6;
Shishir Agrawal566b7612013-10-28 14:41:00 -070070 private static final int CMD_TRANSMIT_APDU = 7;
71 private static final int EVENT_TRANSMIT_APDU_DONE = 8;
72 private static final int CMD_OPEN_CHANNEL = 9;
73 private static final int EVENT_OPEN_CHANNEL_DONE = 10;
74 private static final int CMD_CLOSE_CHANNEL = 11;
75 private static final int EVENT_CLOSE_CHANNEL_DONE = 12;
76
Santos Cordon7d4ddf62013-07-10 11:58:08 -070077
78 /** The singleton instance. */
79 private static PhoneInterfaceManager sInstance;
80
81 PhoneGlobals mApp;
82 Phone mPhone;
83 CallManager mCM;
84 AppOpsManager mAppOps;
85 MainThreadHandler mMainThreadHandler;
Santos Cordon406c0342013-08-28 00:07:47 -070086 CallHandlerServiceProxy mCallHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070087
88 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -070089 * A request object to use for transmitting data to an ICC.
90 */
91 private static final class IccAPDUArgument {
92 public int channel, cla, command, p1, p2, p3;
93 public String data;
94
95 public IccAPDUArgument(int channel, int cla, int command,
96 int p1, int p2, int p3, String data) {
97 this.channel = channel;
98 this.cla = cla;
99 this.command = command;
100 this.p1 = p1;
101 this.p2 = p2;
102 this.p3 = p3;
103 this.data = data;
104 }
105 }
106
107 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700108 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
109 * request after sending. The main thread will notify the request when it is complete.
110 */
111 private static final class MainThreadRequest {
112 /** The argument to use for the request */
113 public Object argument;
114 /** The result of the request that is run on the main thread */
115 public Object result;
116
117 public MainThreadRequest(Object argument) {
118 this.argument = argument;
119 }
120 }
121
122 /**
123 * A handler that processes messages on the main thread in the phone process. Since many
124 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
125 * inbound binder threads to the main thread in the phone process. The Binder thread
126 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
127 * on, which will be notified when the operation completes and will contain the result of the
128 * request.
129 *
130 * <p>If a MainThreadRequest object is provided in the msg.obj field,
131 * note that request.result must be set to something non-null for the calling thread to
132 * unblock.
133 */
134 private final class MainThreadHandler extends Handler {
135 @Override
136 public void handleMessage(Message msg) {
137 MainThreadRequest request;
138 Message onCompleted;
139 AsyncResult ar;
140
141 switch (msg.what) {
142 case CMD_HANDLE_PIN_MMI:
143 request = (MainThreadRequest) msg.obj;
144 request.result = Boolean.valueOf(
145 mPhone.handlePinMmi((String) request.argument));
146 // Wake up the requesting thread
147 synchronized (request) {
148 request.notifyAll();
149 }
150 break;
151
152 case CMD_HANDLE_NEIGHBORING_CELL:
153 request = (MainThreadRequest) msg.obj;
154 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
155 request);
156 mPhone.getNeighboringCids(onCompleted);
157 break;
158
159 case EVENT_NEIGHBORING_CELL_DONE:
160 ar = (AsyncResult) msg.obj;
161 request = (MainThreadRequest) ar.userObj;
162 if (ar.exception == null && ar.result != null) {
163 request.result = ar.result;
164 } else {
165 // create an empty list to notify the waiting thread
166 request.result = new ArrayList<NeighboringCellInfo>();
167 }
168 // Wake up the requesting thread
169 synchronized (request) {
170 request.notifyAll();
171 }
172 break;
173
174 case CMD_ANSWER_RINGING_CALL:
175 answerRingingCallInternal();
176 break;
177
178 case CMD_SILENCE_RINGER:
179 silenceRingerInternal();
180 break;
181
182 case CMD_END_CALL:
183 request = (MainThreadRequest) msg.obj;
184 boolean hungUp = false;
185 int phoneType = mPhone.getPhoneType();
186 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
187 // CDMA: If the user presses the Power button we treat it as
188 // ending the complete call session
189 hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
190 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
191 // GSM: End the call as per the Phone state
192 hungUp = PhoneUtils.hangup(mCM);
193 } else {
194 throw new IllegalStateException("Unexpected phone type: " + phoneType);
195 }
196 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
197 request.result = hungUp;
198 // Wake up the requesting thread
199 synchronized (request) {
200 request.notifyAll();
201 }
202 break;
203
Shishir Agrawal566b7612013-10-28 14:41:00 -0700204 case CMD_TRANSMIT_APDU:
205 request = (MainThreadRequest) msg.obj;
206 IccAPDUArgument argument = (IccAPDUArgument) request.argument;
207 onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_DONE, request);
208 UiccController.getInstance().getUiccCard().iccTransmitApduLogicalChannel(
209 argument.channel, argument.cla, argument.command,
210 argument.p1, argument.p2, argument.p3, argument.data,
211 onCompleted);
212 break;
213
214 case EVENT_TRANSMIT_APDU_DONE:
215 ar = (AsyncResult) msg.obj;
216 request = (MainThreadRequest) ar.userObj;
217 if (ar.exception == null && ar.result != null) {
218 request.result = ar.result;
219 } else {
220 request.result = new IccIoResult(0x6F, 0, (byte[])null);
221 if (ar.result == null) {
222 loge("iccTransmitApduLogicalChannel: Empty response");
223 } else if (ar.exception != null &&
224 (ar.exception instanceof CommandException)) {
225 loge("iccTransmitApduLogicalChannel: CommandException: " +
226 (CommandException)ar.exception);
227 } else {
228 loge("iccTransmitApduLogicalChannel: Unknown exception");
229 }
230 }
231 synchronized (request) {
232 request.notifyAll();
233 }
234 break;
235
236 case CMD_OPEN_CHANNEL:
237 request = (MainThreadRequest) msg.obj;
238 onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request);
239 UiccController.getInstance().getUiccCard().iccOpenLogicalChannel(
240 (String)request.argument, onCompleted);
241 break;
242
243 case EVENT_OPEN_CHANNEL_DONE:
244 ar = (AsyncResult) msg.obj;
245 request = (MainThreadRequest) ar.userObj;
246 if (ar.exception == null && ar.result != null) {
247 request.result = new Integer(((int[])ar.result)[0]);
248 } else {
249 request.result = new Integer(-1);
250 if (ar.result == null) {
251 loge("iccOpenLogicalChannel: Empty response");
252 } else if (ar.exception != null &&
253 (ar.exception instanceof CommandException)) {
254 loge("iccOpenLogicalChannel: CommandException: " +
255 (CommandException)ar.exception);
256 } else {
257 loge("iccOpenLogicalChannel: Unknown exception");
258 }
259 }
260 synchronized (request) {
261 request.notifyAll();
262 }
263 break;
264
265 case CMD_CLOSE_CHANNEL:
266 request = (MainThreadRequest) msg.obj;
267 onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE,
268 request);
269 UiccController.getInstance().getUiccCard().iccCloseLogicalChannel(
270 ((Integer)request.argument).intValue(),
271 onCompleted);
272 break;
273
274 case EVENT_CLOSE_CHANNEL_DONE:
275 ar = (AsyncResult) msg.obj;
276 request = (MainThreadRequest) ar.userObj;
277 if (ar.exception == null) {
278 request.result = new Boolean(true);
279 } else {
280 request.result = new Boolean(false);
281 if (ar.exception instanceof CommandException) {
282 loge("iccCloseLogicalChannel: CommandException: " +
283 (CommandException)ar.exception);
284 } else {
285 loge("iccCloseLogicalChannel: Unknown exception");
286 }
287 }
288 synchronized (request) {
289 request.notifyAll();
290 }
291 break;
292
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700293 default:
294 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
295 break;
296 }
297 }
298 }
299
300 /**
301 * Posts the specified command to be executed on the main thread,
302 * waits for the request to complete, and returns the result.
303 * @see #sendRequestAsync
304 */
305 private Object sendRequest(int command, Object argument) {
306 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
307 throw new RuntimeException("This method will deadlock if called from the main thread.");
308 }
309
310 MainThreadRequest request = new MainThreadRequest(argument);
311 Message msg = mMainThreadHandler.obtainMessage(command, request);
312 msg.sendToTarget();
313
314 // Wait for the request to complete
315 synchronized (request) {
316 while (request.result == null) {
317 try {
318 request.wait();
319 } catch (InterruptedException e) {
320 // Do nothing, go back and wait until the request is complete
321 }
322 }
323 }
324 return request.result;
325 }
326
327 /**
328 * Asynchronous ("fire and forget") version of sendRequest():
329 * Posts the specified command to be executed on the main thread, and
330 * returns immediately.
331 * @see #sendRequest
332 */
333 private void sendRequestAsync(int command) {
334 mMainThreadHandler.sendEmptyMessage(command);
335 }
336
337 /**
338 * Initialize the singleton PhoneInterfaceManager instance.
339 * This is only done once, at startup, from PhoneApp.onCreate().
340 */
Santos Cordon406c0342013-08-28 00:07:47 -0700341 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
342 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700343 synchronized (PhoneInterfaceManager.class) {
344 if (sInstance == null) {
Santos Cordon406c0342013-08-28 00:07:47 -0700345 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700346 } else {
347 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
348 }
349 return sInstance;
350 }
351 }
352
353 /** Private constructor; @see init() */
Santos Cordon406c0342013-08-28 00:07:47 -0700354 private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
355 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700356 mApp = app;
357 mPhone = phone;
358 mCM = PhoneGlobals.getInstance().mCM;
359 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
360 mMainThreadHandler = new MainThreadHandler();
Santos Cordon406c0342013-08-28 00:07:47 -0700361 mCallHandlerService = callHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700362 publish();
363 }
364
365 private void publish() {
366 if (DBG) log("publish: " + this);
367
368 ServiceManager.addService("phone", this);
369 }
370
371 //
372 // Implementation of the ITelephony interface.
373 //
374
375 public void dial(String number) {
376 if (DBG) log("dial: " + number);
377 // No permission check needed here: This is just a wrapper around the
378 // ACTION_DIAL intent, which is available to any app since it puts up
379 // the UI before it does anything.
380
381 String url = createTelUrl(number);
382 if (url == null) {
383 return;
384 }
385
386 // PENDING: should we just silently fail if phone is offhook or ringing?
387 PhoneConstants.State state = mCM.getState();
388 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
389 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
390 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
391 mApp.startActivity(intent);
392 }
393 }
394
395 public void call(String callingPackage, String number) {
396 if (DBG) log("call: " + number);
397
398 // This is just a wrapper around the ACTION_CALL intent, but we still
399 // need to do a permission check since we're calling startActivity()
400 // from the context of the phone app.
401 enforceCallPermission();
402
403 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
404 != AppOpsManager.MODE_ALLOWED) {
405 return;
406 }
407
408 String url = createTelUrl(number);
409 if (url == null) {
410 return;
411 }
412
413 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
414 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
415 mApp.startActivity(intent);
416 }
417
418 private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
Makoto Onukibcf20992013-09-12 17:59:30 -0700419 boolean showDialpad) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700420 if (!PhoneGlobals.sVoiceCapable) {
421 // Never allow the InCallScreen to appear on data-only devices.
422 return false;
423 }
424 if (isIdle()) {
425 return false;
426 }
427 // If the phone isn't idle then go to the in-call screen
428 long callingId = Binder.clearCallingIdentity();
Santos Cordon406c0342013-08-28 00:07:47 -0700429
Makoto Onukibcf20992013-09-12 17:59:30 -0700430 mCallHandlerService.bringToForeground(showDialpad);
Santos Cordon406c0342013-08-28 00:07:47 -0700431
432 Binder.restoreCallingIdentity(callingId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700433 return true;
434 }
435
436 // Show the in-call screen without specifying the initial dialpad state.
437 public boolean showCallScreen() {
438 return showCallScreenInternal(false, false);
439 }
440
441 // The variation of showCallScreen() that specifies the initial dialpad state.
442 // (Ideally this would be called showCallScreen() too, just with a different
443 // signature, but AIDL doesn't allow that.)
444 public boolean showCallScreenWithDialpad(boolean showDialpad) {
445 return showCallScreenInternal(true, showDialpad);
446 }
447
448 /**
449 * End a call based on call state
450 * @return true is a call was ended
451 */
452 public boolean endCall() {
453 enforceCallPermission();
454 return (Boolean) sendRequest(CMD_END_CALL, null);
455 }
456
457 public void answerRingingCall() {
458 if (DBG) log("answerRingingCall...");
459 // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
460 // but that can probably wait till the big TelephonyManager API overhaul.
461 // For now, protect this call with the MODIFY_PHONE_STATE permission.
462 enforceModifyPermission();
463 sendRequestAsync(CMD_ANSWER_RINGING_CALL);
464 }
465
466 /**
467 * Make the actual telephony calls to implement answerRingingCall().
468 * This should only be called from the main thread of the Phone app.
469 * @see #answerRingingCall
470 *
471 * TODO: it would be nice to return true if we answered the call, or
472 * false if there wasn't actually a ringing incoming call, or some
473 * other error occurred. (In other words, pass back the return value
474 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
475 * But that would require calling this method via sendRequest() rather
476 * than sendRequestAsync(), and right now we don't actually *need* that
477 * return value, so let's just return void for now.
478 */
479 private void answerRingingCallInternal() {
480 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
481 if (hasRingingCall) {
482 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
483 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
484 if (hasActiveCall && hasHoldingCall) {
485 // Both lines are in use!
486 // TODO: provide a flag to let the caller specify what
487 // policy to use if both lines are in use. (The current
488 // behavior is hardwired to "answer incoming, end ongoing",
489 // which is how the CALL button is specced to behave.)
490 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
491 return;
492 } else {
493 // answerCall() will automatically hold the current active
494 // call, if there is one.
495 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
496 return;
497 }
498 } else {
499 // No call was ringing.
500 return;
501 }
502 }
503
504 public void silenceRinger() {
505 if (DBG) log("silenceRinger...");
506 // TODO: find a more appropriate permission to check here.
507 // (That can probably wait till the big TelephonyManager API overhaul.
508 // For now, protect this call with the MODIFY_PHONE_STATE permission.)
509 enforceModifyPermission();
510 sendRequestAsync(CMD_SILENCE_RINGER);
511 }
512
513 /**
514 * Internal implemenation of silenceRinger().
515 * This should only be called from the main thread of the Phone app.
516 * @see #silenceRinger
517 */
518 private void silenceRingerInternal() {
519 if ((mCM.getState() == PhoneConstants.State.RINGING)
520 && mApp.notifier.isRinging()) {
521 // Ringer is actually playing, so silence it.
522 if (DBG) log("silenceRingerInternal: silencing...");
523 mApp.notifier.silenceRinger();
524 }
525 }
526
527 public boolean isOffhook() {
528 return (mCM.getState() == PhoneConstants.State.OFFHOOK);
529 }
530
531 public boolean isRinging() {
532 return (mCM.getState() == PhoneConstants.State.RINGING);
533 }
534
535 public boolean isIdle() {
536 return (mCM.getState() == PhoneConstants.State.IDLE);
537 }
538
539 public boolean isSimPinEnabled() {
540 enforceReadPermission();
541 return (PhoneGlobals.getInstance().isSimPinEnabled());
542 }
543
544 public boolean supplyPin(String pin) {
Wink Saville9de0f752013-10-22 19:04:03 -0700545 int [] resultArray = supplyPinReportResult(pin);
546 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
547 }
548
549 public boolean supplyPuk(String puk, String pin) {
550 int [] resultArray = supplyPukReportResult(puk, pin);
551 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
552 }
553
554 /** {@hide} */
555 public int[] supplyPinReportResult(String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700556 enforceModifyPermission();
557 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
558 checkSimPin.start();
559 return checkSimPin.unlockSim(null, pin);
560 }
561
Wink Saville9de0f752013-10-22 19:04:03 -0700562 /** {@hide} */
563 public int[] supplyPukReportResult(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700564 enforceModifyPermission();
565 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
566 checkSimPuk.start();
567 return checkSimPuk.unlockSim(puk, pin);
568 }
569
570 /**
Wink Saville9de0f752013-10-22 19:04:03 -0700571 * Helper thread to turn async call to SimCard#supplyPin into
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700572 * a synchronous one.
573 */
574 private static class UnlockSim extends Thread {
575
576 private final IccCard mSimCard;
577
578 private boolean mDone = false;
Wink Saville9de0f752013-10-22 19:04:03 -0700579 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
580 private int mRetryCount = -1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700581
582 // For replies from SimCard interface
583 private Handler mHandler;
584
585 // For async handler to identify request type
586 private static final int SUPPLY_PIN_COMPLETE = 100;
587
588 public UnlockSim(IccCard simCard) {
589 mSimCard = simCard;
590 }
591
592 @Override
593 public void run() {
594 Looper.prepare();
595 synchronized (UnlockSim.this) {
596 mHandler = new Handler() {
597 @Override
598 public void handleMessage(Message msg) {
599 AsyncResult ar = (AsyncResult) msg.obj;
600 switch (msg.what) {
601 case SUPPLY_PIN_COMPLETE:
602 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
603 synchronized (UnlockSim.this) {
Wink Saville9de0f752013-10-22 19:04:03 -0700604 mRetryCount = msg.arg1;
605 if (ar.exception != null) {
606 if (ar.exception instanceof CommandException &&
607 ((CommandException)(ar.exception)).getCommandError()
608 == CommandException.Error.PASSWORD_INCORRECT) {
609 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
610 } else {
611 mResult = PhoneConstants.PIN_GENERAL_FAILURE;
612 }
613 } else {
614 mResult = PhoneConstants.PIN_RESULT_SUCCESS;
615 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700616 mDone = true;
617 UnlockSim.this.notifyAll();
618 }
619 break;
620 }
621 }
622 };
623 UnlockSim.this.notifyAll();
624 }
625 Looper.loop();
626 }
627
628 /*
629 * Use PIN or PUK to unlock SIM card
630 *
631 * If PUK is null, unlock SIM card with PIN
632 *
633 * If PUK is not null, unlock SIM card with PUK and set PIN code
634 */
Wink Saville9de0f752013-10-22 19:04:03 -0700635 synchronized int[] unlockSim(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700636
637 while (mHandler == null) {
638 try {
639 wait();
640 } catch (InterruptedException e) {
641 Thread.currentThread().interrupt();
642 }
643 }
644 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
645
646 if (puk == null) {
647 mSimCard.supplyPin(pin, callback);
648 } else {
649 mSimCard.supplyPuk(puk, pin, callback);
650 }
651
652 while (!mDone) {
653 try {
654 Log.d(LOG_TAG, "wait for done");
655 wait();
656 } catch (InterruptedException e) {
657 // Restore the interrupted status
658 Thread.currentThread().interrupt();
659 }
660 }
661 Log.d(LOG_TAG, "done");
Wink Saville9de0f752013-10-22 19:04:03 -0700662 int[] resultArray = new int[2];
663 resultArray[0] = mResult;
664 resultArray[1] = mRetryCount;
665 return resultArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700666 }
667 }
668
669 public void updateServiceLocation() {
670 // No permission check needed here: this call is harmless, and it's
671 // needed for the ServiceState.requestStateUpdate() call (which is
672 // already intentionally exposed to 3rd parties.)
673 mPhone.updateServiceLocation();
674 }
675
676 public boolean isRadioOn() {
677 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
678 }
679
680 public void toggleRadioOnOff() {
681 enforceModifyPermission();
682 mPhone.setRadioPower(!isRadioOn());
683 }
684 public boolean setRadio(boolean turnOn) {
685 enforceModifyPermission();
686 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
687 toggleRadioOnOff();
688 }
689 return true;
690 }
691 public boolean setRadioPower(boolean turnOn) {
692 enforceModifyPermission();
693 mPhone.setRadioPower(turnOn);
694 return true;
695 }
696
697 public boolean enableDataConnectivity() {
698 enforceModifyPermission();
699 ConnectivityManager cm =
700 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
701 cm.setMobileDataEnabled(true);
702 return true;
703 }
704
705 public int enableApnType(String type) {
706 enforceModifyPermission();
707 return mPhone.enableApnType(type);
708 }
709
710 public int disableApnType(String type) {
711 enforceModifyPermission();
712 return mPhone.disableApnType(type);
713 }
714
715 public boolean disableDataConnectivity() {
716 enforceModifyPermission();
717 ConnectivityManager cm =
718 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
719 cm.setMobileDataEnabled(false);
720 return true;
721 }
722
723 public boolean isDataConnectivityPossible() {
724 return mPhone.isDataConnectivityPossible();
725 }
726
727 public boolean handlePinMmi(String dialString) {
728 enforceModifyPermission();
729 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
730 }
731
732 public void cancelMissedCallsNotification() {
733 enforceModifyPermission();
734 mApp.notificationMgr.cancelMissedCallNotification();
735 }
736
737 public int getCallState() {
738 return DefaultPhoneNotifier.convertCallState(mCM.getState());
739 }
740
741 public int getDataState() {
742 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
743 }
744
745 public int getDataActivity() {
746 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
747 }
748
749 @Override
750 public Bundle getCellLocation() {
751 try {
752 mApp.enforceCallingOrSelfPermission(
753 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
754 } catch (SecurityException e) {
755 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
756 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
757 // is the weaker precondition
758 mApp.enforceCallingOrSelfPermission(
759 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
760 }
761
762 if (checkIfCallerIsSelfOrForegoundUser()) {
763 if (DBG_LOC) log("getCellLocation: is active user");
764 Bundle data = new Bundle();
765 mPhone.getCellLocation().fillInNotifierBundle(data);
766 return data;
767 } else {
768 if (DBG_LOC) log("getCellLocation: suppress non-active user");
769 return null;
770 }
771 }
772
773 @Override
774 public void enableLocationUpdates() {
775 mApp.enforceCallingOrSelfPermission(
776 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
777 mPhone.enableLocationUpdates();
778 }
779
780 @Override
781 public void disableLocationUpdates() {
782 mApp.enforceCallingOrSelfPermission(
783 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
784 mPhone.disableLocationUpdates();
785 }
786
787 @Override
788 @SuppressWarnings("unchecked")
789 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
790 try {
791 mApp.enforceCallingOrSelfPermission(
792 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
793 } catch (SecurityException e) {
794 // If we have ACCESS_FINE_LOCATION permission, skip the check
795 // for ACCESS_COARSE_LOCATION
796 // A failure should throw the SecurityException from
797 // ACCESS_COARSE_LOCATION since this is the weaker precondition
798 mApp.enforceCallingOrSelfPermission(
799 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
800 }
801
802 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
803 callingPackage) != AppOpsManager.MODE_ALLOWED) {
804 return null;
805 }
806 if (checkIfCallerIsSelfOrForegoundUser()) {
807 if (DBG_LOC) log("getNeighboringCellInfo: is active user");
808
809 ArrayList<NeighboringCellInfo> cells = null;
810
811 try {
812 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
813 CMD_HANDLE_NEIGHBORING_CELL, null);
814 } catch (RuntimeException e) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700815 loge("getNeighboringCellInfo " + e);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700816 }
817 return cells;
818 } else {
819 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
820 return null;
821 }
822 }
823
824
825 @Override
826 public List<CellInfo> getAllCellInfo() {
827 try {
828 mApp.enforceCallingOrSelfPermission(
829 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
830 } catch (SecurityException e) {
831 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
832 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
833 // is the weaker precondition
834 mApp.enforceCallingOrSelfPermission(
835 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
836 }
837
838 if (checkIfCallerIsSelfOrForegoundUser()) {
839 if (DBG_LOC) log("getAllCellInfo: is active user");
840 return mPhone.getAllCellInfo();
841 } else {
842 if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
843 return null;
844 }
845 }
846
847 public void setCellInfoListRate(int rateInMillis) {
848 mPhone.setCellInfoListRate(rateInMillis);
849 }
850
851 //
852 // Internal helper methods.
853 //
854
855 private boolean checkIfCallerIsSelfOrForegoundUser() {
856 boolean ok;
857
858 boolean self = Binder.getCallingUid() == Process.myUid();
859 if (!self) {
860 // Get the caller's user id then clear the calling identity
861 // which will be restored in the finally clause.
862 int callingUser = UserHandle.getCallingUserId();
863 long ident = Binder.clearCallingIdentity();
864
865 try {
866 // With calling identity cleared the current user is the foreground user.
867 int foregroundUser = ActivityManager.getCurrentUser();
868 ok = (foregroundUser == callingUser);
869 if (DBG_LOC) {
870 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
871 + " callingUser=" + callingUser + " ok=" + ok);
872 }
873 } catch (Exception ex) {
874 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
875 ok = false;
876 } finally {
877 Binder.restoreCallingIdentity(ident);
878 }
879 } else {
880 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
881 ok = true;
882 }
883 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
884 return ok;
885 }
886
887 /**
888 * Make sure the caller has the READ_PHONE_STATE permission.
889 *
890 * @throws SecurityException if the caller does not have the required permission
891 */
892 private void enforceReadPermission() {
893 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
894 }
895
896 /**
897 * Make sure the caller has the MODIFY_PHONE_STATE permission.
898 *
899 * @throws SecurityException if the caller does not have the required permission
900 */
901 private void enforceModifyPermission() {
902 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
903 }
904
905 /**
906 * Make sure the caller has the CALL_PHONE permission.
907 *
908 * @throws SecurityException if the caller does not have the required permission
909 */
910 private void enforceCallPermission() {
911 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
912 }
913
Shishir Agrawal566b7612013-10-28 14:41:00 -0700914 /**
915 * Make sure the caller has SIM_COMMUNICATION permission.
916 *
917 * @throws SecurityException if the caller does not have the required permission.
918 */
919 private void enforceSimCommunicationPermission() {
920 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.SIM_COMMUNICATION, null);
921 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700922
923 private String createTelUrl(String number) {
924 if (TextUtils.isEmpty(number)) {
925 return null;
926 }
927
928 StringBuilder buf = new StringBuilder("tel:");
929 buf.append(number);
930 return buf.toString();
931 }
932
933 private void log(String msg) {
934 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
935 }
936
937 private void loge(String msg) {
938 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
939 }
940
941 public int getActivePhoneType() {
942 return mPhone.getPhoneType();
943 }
944
945 /**
946 * Returns the CDMA ERI icon index to display
947 */
948 public int getCdmaEriIconIndex() {
949 return mPhone.getCdmaEriIconIndex();
950 }
951
952 /**
953 * Returns the CDMA ERI icon mode,
954 * 0 - ON
955 * 1 - FLASHING
956 */
957 public int getCdmaEriIconMode() {
958 return mPhone.getCdmaEriIconMode();
959 }
960
961 /**
962 * Returns the CDMA ERI text,
963 */
964 public String getCdmaEriText() {
965 return mPhone.getCdmaEriText();
966 }
967
968 /**
969 * Returns true if CDMA provisioning needs to run.
970 */
971 public boolean needsOtaServiceProvisioning() {
972 return mPhone.needsOtaServiceProvisioning();
973 }
974
975 /**
976 * Returns the unread count of voicemails
977 */
978 public int getVoiceMessageCount() {
979 return mPhone.getVoiceMessageCount();
980 }
981
982 /**
983 * Returns the data network type
984 *
985 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
986 */
987 @Override
988 public int getNetworkType() {
989 return mPhone.getServiceState().getDataNetworkType();
990 }
991
992 /**
993 * Returns the data network type
994 */
995 @Override
996 public int getDataNetworkType() {
997 return mPhone.getServiceState().getDataNetworkType();
998 }
999
1000 /**
1001 * Returns the data network type
1002 */
1003 @Override
1004 public int getVoiceNetworkType() {
1005 return mPhone.getServiceState().getVoiceNetworkType();
1006 }
1007
1008 /**
1009 * @return true if a ICC card is present
1010 */
1011 public boolean hasIccCard() {
1012 return mPhone.getIccCard().hasIccCard();
1013 }
1014
1015 /**
1016 * Return if the current radio is LTE on CDMA. This
1017 * is a tri-state return value as for a period of time
1018 * the mode may be unknown.
1019 *
1020 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
1021 * or {@link PHone#LTE_ON_CDMA_TRUE}
1022 */
1023 public int getLteOnCdmaMode() {
1024 return mPhone.getLteOnCdmaMode();
1025 }
Shishir Agrawal566b7612013-10-28 14:41:00 -07001026
1027 @Override
1028 public int iccOpenLogicalChannel(String AID) {
1029 enforceSimCommunicationPermission();
1030
1031 if (DBG) log("iccOpenLogicalChannel: " + AID);
1032 Integer channel = (Integer)sendRequest(CMD_OPEN_CHANNEL, AID);
1033 if (DBG) log("iccOpenLogicalChannel: " + channel);
1034 return channel.intValue();
1035 }
1036
1037 @Override
1038 public boolean iccCloseLogicalChannel(int channel) {
1039 enforceSimCommunicationPermission();
1040
1041 if (DBG) log("iccCloseLogicalChannel: " + channel);
1042 if (channel < 0) {
1043 return false;
1044 }
1045 Boolean success = (Boolean)sendRequest(CMD_CLOSE_CHANNEL,
1046 new Integer(channel));
1047 if (DBG) log("iccCloseLogicalChannel: " + success);
1048 return success;
1049 }
1050
1051 @Override
1052 public String iccTransmitApduLogicalChannel(int channel, int cla,
1053 int command, int p1, int p2, int p3, String data) {
1054 enforceSimCommunicationPermission();
1055
1056 if (DBG) {
1057 log("iccTransmitApduLogicalChannel: chnl=" + channel + " cla=" + cla +
1058 " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 +
1059 " data=" + data);
1060 }
1061
1062 if (channel < 0) {
1063 return "";
1064 }
1065
1066 IccIoResult response = (IccIoResult)sendRequest(CMD_TRANSMIT_APDU,
1067 new IccAPDUArgument(channel, cla, command, p1, p2, p3, data));
1068 if (DBG) log("iccTransmitApduLogicalChannel: " + response);
1069
1070 // If the payload is null, there was an error. Indicate that by returning
1071 // an empty string.
1072 if (response.payload == null) {
1073 return "";
1074 }
1075
1076 // Append the returned status code to the end of the response payload.
1077 String s = Integer.toHexString(
1078 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1079 s = IccUtils.bytesToHexString(response.payload) + s;
1080 return s;
1081 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001082}