1 /* 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 17 package com.android.phone; 18 19 import android.app.ActivityManager; 20 import android.app.AppOpsManager; 21 import android.content.ActivityNotFoundException; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.net.ConnectivityManager; 25 import android.net.Uri; 26 import android.os.AsyncResult; 27 import android.os.Binder; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.os.Looper; 31 import android.os.Message; 32 import android.os.Process; 33 import android.os.ServiceManager; 34 import android.os.UserHandle; 35 import android.telephony.NeighboringCellInfo; 36 import android.telephony.CellInfo; 37 import android.telephony.ServiceState; 38 import android.text.TextUtils; 39 import android.util.Log; 40 41 import com.android.internal.telephony.DefaultPhoneNotifier; 42 import com.android.internal.telephony.IccCard; 43 import com.android.internal.telephony.ITelephony; 44 import com.android.internal.telephony.Phone; 45 import com.android.internal.telephony.CallManager; 46 import com.android.internal.telephony.PhoneConstants; 47 48 import java.util.List; 49 import java.util.ArrayList; 50 51 /** 52 * Implementation of the ITelephony interface. 53 */ 54 public class PhoneInterfaceManager extends ITelephony.Stub { 55 private static final String LOG_TAG = "PhoneInterfaceManager"; 56 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2); 57 private static final boolean DBG_LOC = false; 58 59 // Message codes used with mMainThreadHandler 60 private static final int CMD_HANDLE_PIN_MMI = 1; 61 private static final int CMD_HANDLE_NEIGHBORING_CELL = 2; 62 private static final int EVENT_NEIGHBORING_CELL_DONE = 3; 63 private static final int CMD_ANSWER_RINGING_CALL = 4; 64 private static final int CMD_END_CALL = 5; // not used yet 65 private static final int CMD_SILENCE_RINGER = 6; 66 67 /** The singleton instance. */ 68 private static PhoneInterfaceManager sInstance; 69 70 PhoneGlobals mApp; 71 Phone mPhone; 72 CallManager mCM; 73 AppOpsManager mAppOps; 74 MainThreadHandler mMainThreadHandler; 75 76 /** 77 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the 78 * request after sending. The main thread will notify the request when it is complete. 79 */ 80 private static final class MainThreadRequest { 81 /** The argument to use for the request */ 82 public Object argument; 83 /** The result of the request that is run on the main thread */ 84 public Object result; 85 86 public MainThreadRequest(Object argument) { 87 this.argument = argument; 88 } 89 } 90 91 /** 92 * A handler that processes messages on the main thread in the phone process. Since many 93 * of the Phone calls are not thread safe this is needed to shuttle the requests from the 94 * inbound binder threads to the main thread in the phone process. The Binder thread 95 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting 96 * on, which will be notified when the operation completes and will contain the result of the 97 * request. 98 * 99 * <p>If a MainThreadRequest object is provided in the msg.obj field, 100 * note that request.result must be set to something non-null for the calling thread to 101 * unblock. 102 */ 103 private final class MainThreadHandler extends Handler { 104 @Override 105 public void handleMessage(Message msg) { 106 MainThreadRequest request; 107 Message onCompleted; 108 AsyncResult ar; 109 110 switch (msg.what) { 111 case CMD_HANDLE_PIN_MMI: 112 request = (MainThreadRequest) msg.obj; 113 request.result = Boolean.valueOf( 114 mPhone.handlePinMmi((String) request.argument)); 115 // Wake up the requesting thread 116 synchronized (request) { 117 request.notifyAll(); 118 } 119 break; 120 121 case CMD_HANDLE_NEIGHBORING_CELL: 122 request = (MainThreadRequest) msg.obj; 123 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE, 124 request); 125 mPhone.getNeighboringCids(onCompleted); 126 break; 127 128 case EVENT_NEIGHBORING_CELL_DONE: 129 ar = (AsyncResult) msg.obj; 130 request = (MainThreadRequest) ar.userObj; 131 if (ar.exception == null && ar.result != null) { 132 request.result = ar.result; 133 } else { 134 // create an empty list to notify the waiting thread 135 request.result = new ArrayList<NeighboringCellInfo>(); 136 } 137 // Wake up the requesting thread 138 synchronized (request) { 139 request.notifyAll(); 140 } 141 break; 142 143 case CMD_ANSWER_RINGING_CALL: 144 answerRingingCallInternal(); 145 break; 146 147 case CMD_SILENCE_RINGER: 148 silenceRingerInternal(); 149 break; 150 151 case CMD_END_CALL: 152 request = (MainThreadRequest) msg.obj; 153 boolean hungUp = false; 154 int phoneType = mPhone.getPhoneType(); 155 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) { 156 // CDMA: If the user presses the Power button we treat it as 157 // ending the complete call session 158 hungUp = PhoneUtils.hangupRingingAndActive(mPhone); 159 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) { 160 // GSM: End the call as per the Phone state 161 hungUp = PhoneUtils.hangup(mCM); 162 } else { 163 throw new IllegalStateException("Unexpected phone type: " + phoneType); 164 } 165 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up")); 166 request.result = hungUp; 167 // Wake up the requesting thread 168 synchronized (request) { 169 request.notifyAll(); 170 } 171 break; 172 173 default: 174 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what); 175 break; 176 } 177 } 178 } 179 180 /** 181 * Posts the specified command to be executed on the main thread, 182 * waits for the request to complete, and returns the result. 183 * @see #sendRequestAsync 184 */ 185 private Object sendRequest(int command, Object argument) { 186 if (Looper.myLooper() == mMainThreadHandler.getLooper()) { 187 throw new RuntimeException("This method will deadlock if called from the main thread."); 188 } 189 190 MainThreadRequest request = new MainThreadRequest(argument); 191 Message msg = mMainThreadHandler.obtainMessage(command, request); 192 msg.sendToTarget(); 193 194 // Wait for the request to complete 195 synchronized (request) { 196 while (request.result == null) { 197 try { 198 request.wait(); 199 } catch (InterruptedException e) { 200 // Do nothing, go back and wait until the request is complete 201 } 202 } 203 } 204 return request.result; 205 } 206 207 /** 208 * Asynchronous ("fire and forget") version of sendRequest(): 209 * Posts the specified command to be executed on the main thread, and 210 * returns immediately. 211 * @see #sendRequest 212 */ 213 private void sendRequestAsync(int command) { 214 mMainThreadHandler.sendEmptyMessage(command); 215 } 216 217 /** 218 * Initialize the singleton PhoneInterfaceManager instance. 219 * This is only done once, at startup, from PhoneApp.onCreate(). 220 */ 221 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone) { 222 synchronized (PhoneInterfaceManager.class) { 223 if (sInstance == null) { 224 sInstance = new PhoneInterfaceManager(app, phone); 225 } else { 226 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 227 } 228 return sInstance; 229 } 230 } 231 232 /** Private constructor; @see init() */ 233 private PhoneInterfaceManager(PhoneGlobals app, Phone phone) { 234 mApp = app; 235 mPhone = phone; 236 mCM = PhoneGlobals.getInstance().mCM; 237 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE); 238 mMainThreadHandler = new MainThreadHandler(); 239 publish(); 240 } 241 242 private void publish() { 243 if (DBG) log("publish: " + this); 244 245 ServiceManager.addService("phone", this); 246 } 247 248 // 249 // Implementation of the ITelephony interface. 250 // 251 252 public void dial(String number) { 253 if (DBG) log("dial: " + number); 254 // No permission check needed here: This is just a wrapper around the 255 // ACTION_DIAL intent, which is available to any app since it puts up 256 // the UI before it does anything. 257 258 String url = createTelUrl(number); 259 if (url == null) { 260 return; 261 } 262 263 // PENDING: should we just silently fail if phone is offhook or ringing? 264 PhoneConstants.State state = mCM.getState(); 265 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) { 266 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url)); 267 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 268 mApp.startActivity(intent); 269 } 270 } 271 272 public void call(String callingPackage, String number) { 273 if (DBG) log("call: " + number); 274 275 // This is just a wrapper around the ACTION_CALL intent, but we still 276 // need to do a permission check since we're calling startActivity() 277 // from the context of the phone app. 278 enforceCallPermission(); 279 280 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage) 281 != AppOpsManager.MODE_ALLOWED) { 282 return; 283 } 284 285 String url = createTelUrl(number); 286 if (url == null) { 287 return; 288 } 289 290 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url)); 291 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 292 mApp.startActivity(intent); 293 } 294 295 private boolean showCallScreenInternal(boolean specifyInitialDialpadState, 296 boolean initialDialpadState) { 297 if (!PhoneGlobals.sVoiceCapable) { 298 // Never allow the InCallScreen to appear on data-only devices. 299 return false; 300 } 301 if (isIdle()) { 302 return false; 303 } 304 // If the phone isn't idle then go to the in-call screen 305 long callingId = Binder.clearCallingIdentity(); 306 try { 307 Intent intent; 308 if (specifyInitialDialpadState) { 309 intent = PhoneGlobals.createInCallIntent(initialDialpadState); 310 } else { 311 intent = PhoneGlobals.createInCallIntent(); 312 } 313 try { 314 mApp.startActivity(intent); 315 } catch (ActivityNotFoundException e) { 316 // It's possible that the in-call UI might not exist 317 // (like on non-voice-capable devices), although we 318 // shouldn't be trying to bring up the InCallScreen on 319 // devices like that in the first place! 320 Log.w(LOG_TAG, "showCallScreenInternal: " 321 + "transition to InCallScreen failed; intent = " + intent); 322 } 323 } finally { 324 Binder.restoreCallingIdentity(callingId); 325 } 326 return true; 327 } 328 329 // Show the in-call screen without specifying the initial dialpad state. 330 public boolean showCallScreen() { 331 return showCallScreenInternal(false, false); 332 } 333 334 // The variation of showCallScreen() that specifies the initial dialpad state. 335 // (Ideally this would be called showCallScreen() too, just with a different 336 // signature, but AIDL doesn't allow that.) 337 public boolean showCallScreenWithDialpad(boolean showDialpad) { 338 return showCallScreenInternal(true, showDialpad); 339 } 340 341 /** 342 * End a call based on call state 343 * @return true is a call was ended 344 */ 345 public boolean endCall() { 346 enforceCallPermission(); 347 return (Boolean) sendRequest(CMD_END_CALL, null); 348 } 349 350 public void answerRingingCall() { 351 if (DBG) log("answerRingingCall..."); 352 // TODO: there should eventually be a separate "ANSWER_PHONE" permission, 353 // but that can probably wait till the big TelephonyManager API overhaul. 354 // For now, protect this call with the MODIFY_PHONE_STATE permission. 355 enforceModifyPermission(); 356 sendRequestAsync(CMD_ANSWER_RINGING_CALL); 357 } 358 359 /** 360 * Make the actual telephony calls to implement answerRingingCall(). 361 * This should only be called from the main thread of the Phone app. 362 * @see #answerRingingCall 363 * 364 * TODO: it would be nice to return true if we answered the call, or 365 * false if there wasn't actually a ringing incoming call, or some 366 * other error occurred. (In other words, pass back the return value 367 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().) 368 * But that would require calling this method via sendRequest() rather 369 * than sendRequestAsync(), and right now we don't actually *need* that 370 * return value, so let's just return void for now. 371 */ 372 private void answerRingingCallInternal() { 373 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle(); 374 if (hasRingingCall) { 375 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle(); 376 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle(); 377 if (hasActiveCall && hasHoldingCall) { 378 // Both lines are in use! 379 // TODO: provide a flag to let the caller specify what 380 // policy to use if both lines are in use. (The current 381 // behavior is hardwired to "answer incoming, end ongoing", 382 // which is how the CALL button is specced to behave.) 383 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall()); 384 return; 385 } else { 386 // answerCall() will automatically hold the current active 387 // call, if there is one. 388 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall()); 389 return; 390 } 391 } else { 392 // No call was ringing. 393 return; 394 } 395 } 396 397 public void silenceRinger() { 398 if (DBG) log("silenceRinger..."); 399 // TODO: find a more appropriate permission to check here. 400 // (That can probably wait till the big TelephonyManager API overhaul. 401 // For now, protect this call with the MODIFY_PHONE_STATE permission.) 402 enforceModifyPermission(); 403 sendRequestAsync(CMD_SILENCE_RINGER); 404 } 405 406 /** 407 * Internal implemenation of silenceRinger(). 408 * This should only be called from the main thread of the Phone app. 409 * @see #silenceRinger 410 */ 411 private void silenceRingerInternal() { 412 if ((mCM.getState() == PhoneConstants.State.RINGING) 413 && mApp.notifier.isRinging()) { 414 // Ringer is actually playing, so silence it. 415 if (DBG) log("silenceRingerInternal: silencing..."); 416 mApp.notifier.silenceRinger(); 417 } 418 } 419 420 public boolean isOffhook() { 421 return (mCM.getState() == PhoneConstants.State.OFFHOOK); 422 } 423 424 public boolean isRinging() { 425 return (mCM.getState() == PhoneConstants.State.RINGING); 426 } 427 428 public boolean isIdle() { 429 return (mCM.getState() == PhoneConstants.State.IDLE); 430 } 431 432 public boolean isSimPinEnabled() { 433 enforceReadPermission(); 434 return (PhoneGlobals.getInstance().isSimPinEnabled()); 435 } 436 437 public boolean supplyPin(String pin) { 438 enforceModifyPermission(); 439 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard()); 440 checkSimPin.start(); 441 return checkSimPin.unlockSim(null, pin); 442 } 443 444 public boolean supplyPuk(String puk, String pin) { 445 enforceModifyPermission(); 446 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard()); 447 checkSimPuk.start(); 448 return checkSimPuk.unlockSim(puk, pin); 449 } 450 451 /** 452 * Helper thread to turn async call to {@link SimCard#supplyPin} into 453 * a synchronous one. 454 */ 455 private static class UnlockSim extends Thread { 456 457 private final IccCard mSimCard; 458 459 private boolean mDone = false; 460 private boolean mResult = false; 461 462 // For replies from SimCard interface 463 private Handler mHandler; 464 465 // For async handler to identify request type 466 private static final int SUPPLY_PIN_COMPLETE = 100; 467 468 public UnlockSim(IccCard simCard) { 469 mSimCard = simCard; 470 } 471 472 @Override 473 public void run() { 474 Looper.prepare(); 475 synchronized (UnlockSim.this) { 476 mHandler = new Handler() { 477 @Override 478 public void handleMessage(Message msg) { 479 AsyncResult ar = (AsyncResult) msg.obj; 480 switch (msg.what) { 481 case SUPPLY_PIN_COMPLETE: 482 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE"); 483 synchronized (UnlockSim.this) { 484 mResult = (ar.exception == null); 485 mDone = true; 486 UnlockSim.this.notifyAll(); 487 } 488 break; 489 } 490 } 491 }; 492 UnlockSim.this.notifyAll(); 493 } 494 Looper.loop(); 495 } 496 497 /* 498 * Use PIN or PUK to unlock SIM card 499 * 500 * If PUK is null, unlock SIM card with PIN 501 * 502 * If PUK is not null, unlock SIM card with PUK and set PIN code 503 */ 504 synchronized boolean unlockSim(String puk, String pin) { 505 506 while (mHandler == null) { 507 try { 508 wait(); 509 } catch (InterruptedException e) { 510 Thread.currentThread().interrupt(); 511 } 512 } 513 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE); 514 515 if (puk == null) { 516 mSimCard.supplyPin(pin, callback); 517 } else { 518 mSimCard.supplyPuk(puk, pin, callback); 519 } 520 521 while (!mDone) { 522 try { 523 Log.d(LOG_TAG, "wait for done"); 524 wait(); 525 } catch (InterruptedException e) { 526 // Restore the interrupted status 527 Thread.currentThread().interrupt(); 528 } 529 } 530 Log.d(LOG_TAG, "done"); 531 return mResult; 532 } 533 } 534 535 public void updateServiceLocation() { 536 // No permission check needed here: this call is harmless, and it's 537 // needed for the ServiceState.requestStateUpdate() call (which is 538 // already intentionally exposed to 3rd parties.) 539 mPhone.updateServiceLocation(); 540 } 541 542 public boolean isRadioOn() { 543 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF; 544 } 545 546 public void toggleRadioOnOff() { 547 enforceModifyPermission(); 548 mPhone.setRadioPower(!isRadioOn()); 549 } 550 public boolean setRadio(boolean turnOn) { 551 enforceModifyPermission(); 552 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) { 553 toggleRadioOnOff(); 554 } 555 return true; 556 } 557 public boolean setRadioPower(boolean turnOn) { 558 enforceModifyPermission(); 559 mPhone.setRadioPower(turnOn); 560 return true; 561 } 562 563 public boolean enableDataConnectivity() { 564 enforceModifyPermission(); 565 ConnectivityManager cm = 566 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE); 567 cm.setMobileDataEnabled(true); 568 return true; 569 } 570 571 public int enableApnType(String type) { 572 enforceModifyPermission(); 573 return mPhone.enableApnType(type); 574 } 575 576 public int disableApnType(String type) { 577 enforceModifyPermission(); 578 return mPhone.disableApnType(type); 579 } 580 581 public boolean disableDataConnectivity() { 582 enforceModifyPermission(); 583 ConnectivityManager cm = 584 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE); 585 cm.setMobileDataEnabled(false); 586 return true; 587 } 588 589 public boolean isDataConnectivityPossible() { 590 return mPhone.isDataConnectivityPossible(); 591 } 592 593 public boolean handlePinMmi(String dialString) { 594 enforceModifyPermission(); 595 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString); 596 } 597 598 public void cancelMissedCallsNotification() { 599 enforceModifyPermission(); 600 mApp.notificationMgr.cancelMissedCallNotification(); 601 } 602 603 public int getCallState() { 604 return DefaultPhoneNotifier.convertCallState(mCM.getState()); 605 } 606 607 public int getDataState() { 608 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState()); 609 } 610 611 public int getDataActivity() { 612 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState()); 613 } 614 615 @Override 616 public Bundle getCellLocation() { 617 try { 618 mApp.enforceCallingOrSelfPermission( 619 android.Manifest.permission.ACCESS_FINE_LOCATION, null); 620 } catch (SecurityException e) { 621 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION 622 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this 623 // is the weaker precondition 624 mApp.enforceCallingOrSelfPermission( 625 android.Manifest.permission.ACCESS_COARSE_LOCATION, null); 626 } 627 628 if (checkIfCallerIsSelfOrForegoundUser()) { 629 if (DBG_LOC) log("getCellLocation: is active user"); 630 Bundle data = new Bundle(); 631 mPhone.getCellLocation().fillInNotifierBundle(data); 632 return data; 633 } else { 634 if (DBG_LOC) log("getCellLocation: suppress non-active user"); 635 return null; 636 } 637 } 638 639 @Override 640 public void enableLocationUpdates() { 641 mApp.enforceCallingOrSelfPermission( 642 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null); 643 mPhone.enableLocationUpdates(); 644 } 645 646 @Override 647 public void disableLocationUpdates() { 648 mApp.enforceCallingOrSelfPermission( 649 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null); 650 mPhone.disableLocationUpdates(); 651 } 652 653 @Override 654 @SuppressWarnings("unchecked") 655 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) { 656 try { 657 mApp.enforceCallingOrSelfPermission( 658 android.Manifest.permission.ACCESS_FINE_LOCATION, null); 659 } catch (SecurityException e) { 660 // If we have ACCESS_FINE_LOCATION permission, skip the check 661 // for ACCESS_COARSE_LOCATION 662 // A failure should throw the SecurityException from 663 // ACCESS_COARSE_LOCATION since this is the weaker precondition 664 mApp.enforceCallingOrSelfPermission( 665 android.Manifest.permission.ACCESS_COARSE_LOCATION, null); 666 } 667 668 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(), 669 callingPackage) != AppOpsManager.MODE_ALLOWED) { 670 return null; 671 } 672 if (checkIfCallerIsSelfOrForegoundUser()) { 673 if (DBG_LOC) log("getNeighboringCellInfo: is active user"); 674 675 ArrayList<NeighboringCellInfo> cells = null; 676 677 try { 678 cells = (ArrayList<NeighboringCellInfo>) sendRequest( 679 CMD_HANDLE_NEIGHBORING_CELL, null); 680 } catch (RuntimeException e) { 681 Log.e(LOG_TAG, "getNeighboringCellInfo " + e); 682 } 683 return cells; 684 } else { 685 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user"); 686 return null; 687 } 688 } 689 690 691 @Override 692 public List<CellInfo> getAllCellInfo() { 693 try { 694 mApp.enforceCallingOrSelfPermission( 695 android.Manifest.permission.ACCESS_FINE_LOCATION, null); 696 } catch (SecurityException e) { 697 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION 698 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this 699 // is the weaker precondition 700 mApp.enforceCallingOrSelfPermission( 701 android.Manifest.permission.ACCESS_COARSE_LOCATION, null); 702 } 703 704 if (checkIfCallerIsSelfOrForegoundUser()) { 705 if (DBG_LOC) log("getAllCellInfo: is active user"); 706 return mPhone.getAllCellInfo(); 707 } else { 708 if (DBG_LOC) log("getAllCellInfo: suppress non-active user"); 709 return null; 710 } 711 } 712 713 public void setCellInfoListRate(int rateInMillis) { 714 mPhone.setCellInfoListRate(rateInMillis); 715 } 716 717 // 718 // Internal helper methods. 719 // 720 721 private boolean checkIfCallerIsSelfOrForegoundUser() { 722 boolean ok; 723 724 boolean self = Binder.getCallingUid() == Process.myUid(); 725 if (!self) { 726 // Get the caller's user id then clear the calling identity 727 // which will be restored in the finally clause. 728 int callingUser = UserHandle.getCallingUserId(); 729 long ident = Binder.clearCallingIdentity(); 730 731 try { 732 // With calling identity cleared the current user is the foreground user. 733 int foregroundUser = ActivityManager.getCurrentUser(); 734 ok = (foregroundUser == callingUser); 735 if (DBG_LOC) { 736 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser 737 + " callingUser=" + callingUser + " ok=" + ok); 738 } 739 } catch (Exception ex) { 740 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex); 741 ok = false; 742 } finally { 743 Binder.restoreCallingIdentity(ident); 744 } 745 } else { 746 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self"); 747 ok = true; 748 } 749 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok); 750 return ok; 751 } 752 753 /** 754 * Make sure the caller has the READ_PHONE_STATE permission. 755 * 756 * @throws SecurityException if the caller does not have the required permission 757 */ 758 private void enforceReadPermission() { 759 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null); 760 } 761 762 /** 763 * Make sure the caller has the MODIFY_PHONE_STATE permission. 764 * 765 * @throws SecurityException if the caller does not have the required permission 766 */ 767 private void enforceModifyPermission() { 768 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null); 769 } 770 771 /** 772 * Make sure the caller has the CALL_PHONE permission. 773 * 774 * @throws SecurityException if the caller does not have the required permission 775 */ 776 private void enforceCallPermission() { 777 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null); 778 } 779 780 781 private String createTelUrl(String number) { 782 if (TextUtils.isEmpty(number)) { 783 return null; 784 } 785 786 StringBuilder buf = new StringBuilder("tel:"); 787 buf.append(number); 788 return buf.toString(); 789 } 790 791 private void log(String msg) { 792 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg); 793 } 794 795 private void loge(String msg) { 796 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg); 797 } 798 799 public int getActivePhoneType() { 800 return mPhone.getPhoneType(); 801 } 802 803 /** 804 * Returns the CDMA ERI icon index to display 805 */ 806 public int getCdmaEriIconIndex() { 807 return mPhone.getCdmaEriIconIndex(); 808 } 809 810 /** 811 * Returns the CDMA ERI icon mode, 812 * 0 - ON 813 * 1 - FLASHING 814 */ 815 public int getCdmaEriIconMode() { 816 return mPhone.getCdmaEriIconMode(); 817 } 818 819 /** 820 * Returns the CDMA ERI text, 821 */ 822 public String getCdmaEriText() { 823 return mPhone.getCdmaEriText(); 824 } 825 826 /** 827 * Returns true if CDMA provisioning needs to run. 828 */ 829 public boolean needsOtaServiceProvisioning() { 830 return mPhone.needsOtaServiceProvisioning(); 831 } 832 833 /** 834 * Returns the unread count of voicemails 835 */ 836 public int getVoiceMessageCount() { 837 return mPhone.getVoiceMessageCount(); 838 } 839 840 /** 841 * Returns the data network type 842 * 843 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}. 844 */ 845 @Override 846 public int getNetworkType() { 847 return mPhone.getServiceState().getDataNetworkType(); 848 } 849 850 /** 851 * Returns the data network type 852 */ 853 @Override 854 public int getDataNetworkType() { 855 return mPhone.getServiceState().getDataNetworkType(); 856 } 857 858 /** 859 * Returns the data network type 860 */ 861 @Override 862 public int getVoiceNetworkType() { 863 return mPhone.getServiceState().getVoiceNetworkType(); 864 } 865 866 /** 867 * @return true if a ICC card is present 868 */ 869 public boolean hasIccCard() { 870 return mPhone.getIccCard().hasIccCard(); 871 } 872 873 /** 874 * Return if the current radio is LTE on CDMA. This 875 * is a tri-state return value as for a period of time 876 * the mode may be unknown. 877 * 878 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE} 879 * or {@link PHone#LTE_ON_CDMA_TRUE} 880 */ 881 public int getLteOnCdmaMode() { 882 return mPhone.getLteOnCdmaMode(); 883 } 884 } 885