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