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