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