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