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.bluetooth.IBluetoothHeadsetPhone; 22 import android.content.ActivityNotFoundException; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.net.ConnectivityManager; 26 import android.net.Uri; 27 import android.os.AsyncResult; 28 import android.os.Binder; 29 import android.os.Bundle; 30 import android.os.Handler; 31 import android.os.IBinder; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.Process; 35 import android.os.RemoteException; 36 import android.os.ServiceManager; 37 import android.os.UserHandle; 38 import android.telephony.NeighboringCellInfo; 39 import android.telephony.CellInfo; 40 import android.telephony.ServiceState; 41 import android.text.TextUtils; 42 import android.util.Log; 43 44 import com.android.internal.telephony.CallManager; 45 import com.android.internal.telephony.CommandException; 46 import com.android.internal.telephony.Connection; 47 import com.android.internal.telephony.DefaultPhoneNotifier; 48 import com.android.internal.telephony.IccCard; 49 import com.android.internal.telephony.ITelephony; 50 import com.android.internal.telephony.ITelephonyListener; 51 import com.android.internal.telephony.Phone; 52 import com.android.internal.telephony.PhoneConstants; 53 import com.android.services.telephony.common.Call; 54 55 import com.android.internal.util.HexDump; 56 57 import java.util.ArrayList; 58 import java.util.HashMap; 59 import java.util.Iterator; 60 import java.util.List; 61 import java.util.Map; 62 63 /** 64 * Implementation of the ITelephony interface. 65 */ 66 public class PhoneInterfaceManager extends ITelephony.Stub implements CallModeler.Listener { 67 private static final String LOG_TAG = "PhoneInterfaceManager"; 68 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2); 69 private static final boolean DBG_LOC = false; 70 71 // Message codes used with mMainThreadHandler 72 private static final int CMD_HANDLE_PIN_MMI = 1; 73 private static final int CMD_HANDLE_NEIGHBORING_CELL = 2; 74 private static final int EVENT_NEIGHBORING_CELL_DONE = 3; 75 private static final int CMD_ANSWER_RINGING_CALL = 4; 76 private static final int CMD_END_CALL = 5; // not used yet 77 private static final int CMD_SILENCE_RINGER = 6; 78 79 /** The singleton instance. */ 80 private static PhoneInterfaceManager sInstance; 81 82 PhoneGlobals mApp; 83 Phone mPhone; 84 CallManager mCM; 85 AppOpsManager mAppOps; 86 MainThreadHandler mMainThreadHandler; 87 CallHandlerServiceProxy mCallHandlerService; 88 CallModeler mCallModeler; 89 DTMFTonePlayer mDtmfTonePlayer; 90 Handler mDtmfStopHandler = new Handler(); 91 Runnable mDtmfStopRunnable; 92 93 private final List<ITelephonyListener> mListeners = new ArrayList<ITelephonyListener>(); 94 private final Map<IBinder, TelephonyListenerDeathRecipient> mDeathRecipients = 95 new HashMap<IBinder, TelephonyListenerDeathRecipient>(); 96 97 /** 98 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the 99 * request after sending. The main thread will notify the request when it is complete. 100 */ 101 private static final class MainThreadRequest { 102 /** The argument to use for the request */ 103 public Object argument; 104 /** The result of the request that is run on the main thread */ 105 public Object result; 106 107 public MainThreadRequest(Object argument) { 108 this.argument = argument; 109 } 110 } 111 112 /** 113 * A handler that processes messages on the main thread in the phone process. Since many 114 * of the Phone calls are not thread safe this is needed to shuttle the requests from the 115 * inbound binder threads to the main thread in the phone process. The Binder thread 116 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting 117 * on, which will be notified when the operation completes and will contain the result of the 118 * request. 119 * 120 * <p>If a MainThreadRequest object is provided in the msg.obj field, 121 * note that request.result must be set to something non-null for the calling thread to 122 * unblock. 123 */ 124 private final class MainThreadHandler extends Handler { 125 @Override 126 public void handleMessage(Message msg) { 127 MainThreadRequest request; 128 Message onCompleted; 129 AsyncResult ar; 130 131 switch (msg.what) { 132 case CMD_HANDLE_PIN_MMI: 133 request = (MainThreadRequest) msg.obj; 134 request.result = Boolean.valueOf( 135 mPhone.handlePinMmi((String) request.argument)); 136 // Wake up the requesting thread 137 synchronized (request) { 138 request.notifyAll(); 139 } 140 break; 141 142 case CMD_HANDLE_NEIGHBORING_CELL: 143 request = (MainThreadRequest) msg.obj; 144 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE, 145 request); 146 mPhone.getNeighboringCids(onCompleted); 147 break; 148 149 case EVENT_NEIGHBORING_CELL_DONE: 150 ar = (AsyncResult) msg.obj; 151 request = (MainThreadRequest) ar.userObj; 152 if (ar.exception == null && ar.result != null) { 153 request.result = ar.result; 154 } else { 155 // create an empty list to notify the waiting thread 156 request.result = new ArrayList<NeighboringCellInfo>(); 157 } 158 // Wake up the requesting thread 159 synchronized (request) { 160 request.notifyAll(); 161 } 162 break; 163 164 case CMD_ANSWER_RINGING_CALL: 165 answerRingingCallInternal(); 166 break; 167 168 case CMD_SILENCE_RINGER: 169 silenceRingerInternal(); 170 break; 171 172 case CMD_END_CALL: 173 request = (MainThreadRequest) msg.obj; 174 boolean hungUp = false; 175 int phoneType = mPhone.getPhoneType(); 176 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) { 177 // CDMA: If the user presses the Power button we treat it as 178 // ending the complete call session 179 hungUp = PhoneUtils.hangupRingingAndActive(mPhone); 180 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) { 181 // GSM: End the call as per the Phone state 182 hungUp = PhoneUtils.hangup(mCM); 183 } else { 184 throw new IllegalStateException("Unexpected phone type: " + phoneType); 185 } 186 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up")); 187 request.result = hungUp; 188 // Wake up the requesting thread 189 synchronized (request) { 190 request.notifyAll(); 191 } 192 break; 193 194 default: 195 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what); 196 break; 197 } 198 } 199 } 200 201 /** 202 * Posts the specified command to be executed on the main thread, 203 * waits for the request to complete, and returns the result. 204 * @see #sendRequestAsync 205 */ 206 private Object sendRequest(int command, Object argument) { 207 if (Looper.myLooper() == mMainThreadHandler.getLooper()) { 208 throw new RuntimeException("This method will deadlock if called from the main thread."); 209 } 210 211 MainThreadRequest request = new MainThreadRequest(argument); 212 Message msg = mMainThreadHandler.obtainMessage(command, request); 213 msg.sendToTarget(); 214 215 // Wait for the request to complete 216 synchronized (request) { 217 while (request.result == null) { 218 try { 219 request.wait(); 220 } catch (InterruptedException e) { 221 // Do nothing, go back and wait until the request is complete 222 } 223 } 224 } 225 return request.result; 226 } 227 228 /** 229 * Asynchronous ("fire and forget") version of sendRequest(): 230 * Posts the specified command to be executed on the main thread, and 231 * returns immediately. 232 * @see #sendRequest 233 */ 234 private void sendRequestAsync(int command) { 235 mMainThreadHandler.sendEmptyMessage(command); 236 } 237 238 /** 239 * Initialize the singleton PhoneInterfaceManager instance. 240 * This is only done once, at startup, from PhoneApp.onCreate(). 241 */ 242 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone, 243 CallHandlerServiceProxy callHandlerService, CallModeler callModeler, 244 DTMFTonePlayer dtmfTonePlayer) { 245 synchronized (PhoneInterfaceManager.class) { 246 if (sInstance == null) { 247 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService, callModeler, 248 dtmfTonePlayer); 249 } else { 250 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 251 } 252 return sInstance; 253 } 254 } 255 256 /** Private constructor; @see init() */ 257 private PhoneInterfaceManager(PhoneGlobals app, Phone phone, 258 CallHandlerServiceProxy callHandlerService, CallModeler callModeler, 259 DTMFTonePlayer dtmfTonePlayer) { 260 mApp = app; 261 mPhone = phone; 262 mCM = PhoneGlobals.getInstance().mCM; 263 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE); 264 mMainThreadHandler = new MainThreadHandler(); 265 mCallHandlerService = callHandlerService; 266 mCallModeler = callModeler; 267 mCallModeler.addListener(this); 268 mDtmfTonePlayer = dtmfTonePlayer; 269 publish(); 270 } 271 272 private void publish() { 273 if (DBG) log("publish: " + this); 274 275 ServiceManager.addService("phone", this); 276 } 277 278 // 279 // Implementation of the ITelephony interface. 280 // 281 282 public void dial(String number) { 283 if (DBG) log("dial: " + number); 284 // No permission check needed here: This is just a wrapper around the 285 // ACTION_DIAL intent, which is available to any app since it puts up 286 // the UI before it does anything. 287 288 String url = createTelUrl(number); 289 if (url == null) { 290 return; 291 } 292 293 // PENDING: should we just silently fail if phone is offhook or ringing? 294 PhoneConstants.State state = mCM.getState(); 295 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) { 296 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url)); 297 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 298 mApp.startActivity(intent); 299 } 300 } 301 302 public void call(String callingPackage, String number) { 303 if (DBG) log("call: " + number); 304 305 // This is just a wrapper around the ACTION_CALL intent, but we still 306 // need to do a permission check since we're calling startActivity() 307 // from the context of the phone app. 308 enforceCallPermission(); 309 310 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage) 311 != AppOpsManager.MODE_ALLOWED) { 312 return; 313 } 314 315 String url = createTelUrl(number); 316 if (url == null) { 317 return; 318 } 319 320 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url)); 321 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 322 mApp.startActivity(intent); 323 } 324 325 private boolean showCallScreenInternal(boolean specifyInitialDialpadState, 326 boolean showDialpad) { 327 if (!PhoneGlobals.sVoiceCapable) { 328 // Never allow the InCallScreen to appear on data-only devices. 329 return false; 330 } 331 if (isIdle()) { 332 return false; 333 } 334 // If the phone isn't idle then go to the in-call screen 335 long callingId = Binder.clearCallingIdentity(); 336 337 mCallHandlerService.bringToForeground(showDialpad); 338 339 Binder.restoreCallingIdentity(callingId); 340 return true; 341 } 342 343 // Show the in-call screen without specifying the initial dialpad state. 344 public boolean showCallScreen() { 345 return showCallScreenInternal(false, false); 346 } 347 348 // The variation of showCallScreen() that specifies the initial dialpad state. 349 // (Ideally this would be called showCallScreen() too, just with a different 350 // signature, but AIDL doesn't allow that.) 351 public boolean showCallScreenWithDialpad(boolean showDialpad) { 352 return showCallScreenInternal(true, showDialpad); 353 } 354 355 /** 356 * End a call based on call state 357 * @return true is a call was ended 358 */ 359 public boolean endCall() { 360 enforceCallPermission(); 361 return (Boolean) sendRequest(CMD_END_CALL, null); 362 } 363 364 public void answerRingingCall() { 365 if (DBG) log("answerRingingCall..."); 366 // TODO: there should eventually be a separate "ANSWER_PHONE" permission, 367 // but that can probably wait till the big TelephonyManager API overhaul. 368 // For now, protect this call with the MODIFY_PHONE_STATE permission. 369 enforceModifyPermission(); 370 sendRequestAsync(CMD_ANSWER_RINGING_CALL); 371 } 372 373 /** 374 * Make the actual telephony calls to implement answerRingingCall(). 375 * This should only be called from the main thread of the Phone app. 376 * @see #answerRingingCall 377 * 378 * TODO: it would be nice to return true if we answered the call, or 379 * false if there wasn't actually a ringing incoming call, or some 380 * other error occurred. (In other words, pass back the return value 381 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().) 382 * But that would require calling this method via sendRequest() rather 383 * than sendRequestAsync(), and right now we don't actually *need* that 384 * return value, so let's just return void for now. 385 */ 386 private void answerRingingCallInternal() { 387 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle(); 388 if (hasRingingCall) { 389 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle(); 390 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle(); 391 if (hasActiveCall && hasHoldingCall) { 392 // Both lines are in use! 393 // TODO: provide a flag to let the caller specify what 394 // policy to use if both lines are in use. (The current 395 // behavior is hardwired to "answer incoming, end ongoing", 396 // which is how the CALL button is specced to behave.) 397 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall()); 398 return; 399 } else { 400 // answerCall() will automatically hold the current active 401 // call, if there is one. 402 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall()); 403 return; 404 } 405 } else { 406 // No call was ringing. 407 return; 408 } 409 } 410 411 public void silenceRinger() { 412 if (DBG) log("silenceRinger..."); 413 // TODO: find a more appropriate permission to check here. 414 // (That can probably wait till the big TelephonyManager API overhaul. 415 // For now, protect this call with the MODIFY_PHONE_STATE permission.) 416 enforceModifyPermission(); 417 sendRequestAsync(CMD_SILENCE_RINGER); 418 } 419 420 /** 421 * Internal implemenation of silenceRinger(). 422 * This should only be called from the main thread of the Phone app. 423 * @see #silenceRinger 424 */ 425 private void silenceRingerInternal() { 426 if ((mCM.getState() == PhoneConstants.State.RINGING) 427 && mApp.notifier.isRinging()) { 428 // Ringer is actually playing, so silence it. 429 if (DBG) log("silenceRingerInternal: silencing..."); 430 mApp.notifier.silenceRinger(); 431 } 432 } 433 434 public boolean isOffhook() { 435 return (mCM.getState() == PhoneConstants.State.OFFHOOK); 436 } 437 438 public boolean isRinging() { 439 return (mCM.getState() == PhoneConstants.State.RINGING); 440 } 441 442 public boolean isIdle() { 443 return (mCM.getState() == PhoneConstants.State.IDLE); 444 } 445 446 public boolean isSimPinEnabled() { 447 enforceReadPermission(); 448 return (PhoneGlobals.getInstance().isSimPinEnabled()); 449 } 450 451 public boolean supplyPin(String pin) { 452 int [] resultArray = supplyPinReportResult(pin); 453 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false; 454 } 455 456 public boolean supplyPuk(String puk, String pin) { 457 int [] resultArray = supplyPukReportResult(puk, pin); 458 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false; 459 } 460 461 /** {@hide} */ 462 public int[] supplyPinReportResult(String pin) { 463 enforceModifyPermission(); 464 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard()); 465 checkSimPin.start(); 466 return checkSimPin.unlockSim(null, pin); 467 } 468 469 /** {@hide} */ 470 public int[] supplyPukReportResult(String puk, String pin) { 471 enforceModifyPermission(); 472 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard()); 473 checkSimPuk.start(); 474 return checkSimPuk.unlockSim(puk, pin); 475 } 476 477 /** 478 * Helper thread to turn async call to SimCard#supplyPin into 479 * a synchronous one. 480 */ 481 private static class UnlockSim extends Thread { 482 483 private final IccCard mSimCard; 484 485 private boolean mDone = false; 486 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE; 487 private int mRetryCount = -1; 488 489 // For replies from SimCard interface 490 private Handler mHandler; 491 492 // For async handler to identify request type 493 private static final int SUPPLY_PIN_COMPLETE = 100; 494 495 public UnlockSim(IccCard simCard) { 496 mSimCard = simCard; 497 } 498 499 @Override 500 public void run() { 501 Looper.prepare(); 502 synchronized (UnlockSim.this) { 503 mHandler = new Handler() { 504 @Override 505 public void handleMessage(Message msg) { 506 AsyncResult ar = (AsyncResult) msg.obj; 507 switch (msg.what) { 508 case SUPPLY_PIN_COMPLETE: 509 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE"); 510 synchronized (UnlockSim.this) { 511 mRetryCount = msg.arg1; 512 if (ar.exception != null) { 513 if (ar.exception instanceof CommandException && 514 ((CommandException)(ar.exception)).getCommandError() 515 == CommandException.Error.PASSWORD_INCORRECT) { 516 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT; 517 } else { 518 mResult = PhoneConstants.PIN_GENERAL_FAILURE; 519 } 520 } else { 521 mResult = PhoneConstants.PIN_RESULT_SUCCESS; 522 } 523 mDone = true; 524 UnlockSim.this.notifyAll(); 525 } 526 break; 527 } 528 } 529 }; 530 UnlockSim.this.notifyAll(); 531 } 532 Looper.loop(); 533 } 534 535 /* 536 * Use PIN or PUK to unlock SIM card 537 * 538 * If PUK is null, unlock SIM card with PIN 539 * 540 * If PUK is not null, unlock SIM card with PUK and set PIN code 541 */ 542 synchronized int[] unlockSim(String puk, String pin) { 543 544 while (mHandler == null) { 545 try { 546 wait(); 547 } catch (InterruptedException e) { 548 Thread.currentThread().interrupt(); 549 } 550 } 551 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE); 552 553 if (puk == null) { 554 mSimCard.supplyPin(pin, callback); 555 } else { 556 mSimCard.supplyPuk(puk, pin, callback); 557 } 558 559 while (!mDone) { 560 try { 561 Log.d(LOG_TAG, "wait for done"); 562 wait(); 563 } catch (InterruptedException e) { 564 // Restore the interrupted status 565 Thread.currentThread().interrupt(); 566 } 567 } 568 Log.d(LOG_TAG, "done"); 569 int[] resultArray = new int[2]; 570 resultArray[0] = mResult; 571 resultArray[1] = mRetryCount; 572 return resultArray; 573 } 574 } 575 576 public void updateServiceLocation() { 577 // No permission check needed here: this call is harmless, and it's 578 // needed for the ServiceState.requestStateUpdate() call (which is 579 // already intentionally exposed to 3rd parties.) 580 mPhone.updateServiceLocation(); 581 } 582 583 public boolean isRadioOn() { 584 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF; 585 } 586 587 public void toggleRadioOnOff() { 588 enforceModifyPermission(); 589 mPhone.setRadioPower(!isRadioOn()); 590 } 591 public boolean setRadio(boolean turnOn) { 592 enforceModifyPermission(); 593 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) { 594 toggleRadioOnOff(); 595 } 596 return true; 597 } 598 public boolean setRadioPower(boolean turnOn) { 599 enforceModifyPermission(); 600 mPhone.setRadioPower(turnOn); 601 return true; 602 } 603 604 public boolean enableDataConnectivity() { 605 enforceModifyPermission(); 606 ConnectivityManager cm = 607 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE); 608 cm.setMobileDataEnabled(true); 609 return true; 610 } 611 612 public int enableApnType(String type) { 613 enforceModifyPermission(); 614 return mPhone.enableApnType(type); 615 } 616 617 public int disableApnType(String type) { 618 enforceModifyPermission(); 619 return mPhone.disableApnType(type); 620 } 621 622 public boolean disableDataConnectivity() { 623 enforceModifyPermission(); 624 ConnectivityManager cm = 625 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE); 626 cm.setMobileDataEnabled(false); 627 return true; 628 } 629 630 public boolean isDataConnectivityPossible() { 631 return mPhone.isDataConnectivityPossible(); 632 } 633 634 public boolean handlePinMmi(String dialString) { 635 enforceModifyPermission(); 636 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString); 637 } 638 639 public void cancelMissedCallsNotification() { 640 enforceModifyPermission(); 641 mApp.notificationMgr.cancelMissedCallNotification(); 642 } 643 644 public int getCallState() { 645 return DefaultPhoneNotifier.convertCallState(mCM.getState()); 646 } 647 648 public int getDataState() { 649 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState()); 650 } 651 652 public int getDataActivity() { 653 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState()); 654 } 655 656 @Override 657 public Bundle getCellLocation() { 658 try { 659 mApp.enforceCallingOrSelfPermission( 660 android.Manifest.permission.ACCESS_FINE_LOCATION, null); 661 } catch (SecurityException e) { 662 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION 663 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this 664 // is the weaker precondition 665 mApp.enforceCallingOrSelfPermission( 666 android.Manifest.permission.ACCESS_COARSE_LOCATION, null); 667 } 668 669 if (checkIfCallerIsSelfOrForegoundUser()) { 670 if (DBG_LOC) log("getCellLocation: is active user"); 671 Bundle data = new Bundle(); 672 mPhone.getCellLocation().fillInNotifierBundle(data); 673 return data; 674 } else { 675 if (DBG_LOC) log("getCellLocation: suppress non-active user"); 676 return null; 677 } 678 } 679 680 @Override 681 public void enableLocationUpdates() { 682 mApp.enforceCallingOrSelfPermission( 683 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null); 684 mPhone.enableLocationUpdates(); 685 } 686 687 @Override 688 public void disableLocationUpdates() { 689 mApp.enforceCallingOrSelfPermission( 690 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null); 691 mPhone.disableLocationUpdates(); 692 } 693 694 @Override 695 @SuppressWarnings("unchecked") 696 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) { 697 try { 698 mApp.enforceCallingOrSelfPermission( 699 android.Manifest.permission.ACCESS_FINE_LOCATION, null); 700 } catch (SecurityException e) { 701 // If we have ACCESS_FINE_LOCATION permission, skip the check 702 // for ACCESS_COARSE_LOCATION 703 // A failure should throw the SecurityException from 704 // ACCESS_COARSE_LOCATION since this is the weaker precondition 705 mApp.enforceCallingOrSelfPermission( 706 android.Manifest.permission.ACCESS_COARSE_LOCATION, null); 707 } 708 709 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(), 710 callingPackage) != AppOpsManager.MODE_ALLOWED) { 711 return null; 712 } 713 if (checkIfCallerIsSelfOrForegoundUser()) { 714 if (DBG_LOC) log("getNeighboringCellInfo: is active user"); 715 716 ArrayList<NeighboringCellInfo> cells = null; 717 718 try { 719 cells = (ArrayList<NeighboringCellInfo>) sendRequest( 720 CMD_HANDLE_NEIGHBORING_CELL, null); 721 } catch (RuntimeException e) { 722 Log.e(LOG_TAG, "getNeighboringCellInfo " + e); 723 } 724 return cells; 725 } else { 726 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user"); 727 return null; 728 } 729 } 730 731 732 @Override 733 public List<CellInfo> getAllCellInfo() { 734 try { 735 mApp.enforceCallingOrSelfPermission( 736 android.Manifest.permission.ACCESS_FINE_LOCATION, null); 737 } catch (SecurityException e) { 738 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION 739 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this 740 // is the weaker precondition 741 mApp.enforceCallingOrSelfPermission( 742 android.Manifest.permission.ACCESS_COARSE_LOCATION, null); 743 } 744 745 if (checkIfCallerIsSelfOrForegoundUser()) { 746 if (DBG_LOC) log("getAllCellInfo: is active user"); 747 return mPhone.getAllCellInfo(); 748 } else { 749 if (DBG_LOC) log("getAllCellInfo: suppress non-active user"); 750 return null; 751 } 752 } 753 754 public void setCellInfoListRate(int rateInMillis) { 755 mPhone.setCellInfoListRate(rateInMillis); 756 } 757 758 // 759 // Internal helper methods. 760 // 761 762 private boolean checkIfCallerIsSelfOrForegoundUser() { 763 boolean ok; 764 765 boolean self = Binder.getCallingUid() == Process.myUid(); 766 if (!self) { 767 // Get the caller's user id then clear the calling identity 768 // which will be restored in the finally clause. 769 int callingUser = UserHandle.getCallingUserId(); 770 long ident = Binder.clearCallingIdentity(); 771 772 try { 773 // With calling identity cleared the current user is the foreground user. 774 int foregroundUser = ActivityManager.getCurrentUser(); 775 ok = (foregroundUser == callingUser); 776 if (DBG_LOC) { 777 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser 778 + " callingUser=" + callingUser + " ok=" + ok); 779 } 780 } catch (Exception ex) { 781 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex); 782 ok = false; 783 } finally { 784 Binder.restoreCallingIdentity(ident); 785 } 786 } else { 787 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self"); 788 ok = true; 789 } 790 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok); 791 return ok; 792 } 793 794 /** 795 * Make sure the caller has the READ_PHONE_STATE permission. 796 * 797 * @throws SecurityException if the caller does not have the required permission 798 */ 799 private void enforceReadPermission() { 800 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null); 801 } 802 803 /** 804 * Make sure the caller has the MODIFY_PHONE_STATE permission. 805 * 806 * @throws SecurityException if the caller does not have the required permission 807 */ 808 private void enforceModifyPermission() { 809 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null); 810 } 811 812 /** 813 * Make sure the caller has the CALL_PHONE permission. 814 * 815 * @throws SecurityException if the caller does not have the required permission 816 */ 817 private void enforceCallPermission() { 818 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null); 819 } 820 821 /** 822 * Make sure the caller has the READ_PRIVILEGED_PHONE_STATE permission. 823 * 824 * @throws SecurityException if the caller does not have the required permission 825 */ 826 private void enforcePrivilegedPhoneStatePermission() { 827 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 828 null); 829 } 830 831 private String createTelUrl(String number) { 832 if (TextUtils.isEmpty(number)) { 833 return null; 834 } 835 836 StringBuilder buf = new StringBuilder("tel:"); 837 buf.append(number); 838 return buf.toString(); 839 } 840 841 private void log(String msg) { 842 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg); 843 } 844 845 private void loge(String msg) { 846 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg); 847 } 848 849 public int getActivePhoneType() { 850 return mPhone.getPhoneType(); 851 } 852 853 /** 854 * Returns the CDMA ERI icon index to display 855 */ 856 public int getCdmaEriIconIndex() { 857 return mPhone.getCdmaEriIconIndex(); 858 } 859 860 /** 861 * Returns the CDMA ERI icon mode, 862 * 0 - ON 863 * 1 - FLASHING 864 */ 865 public int getCdmaEriIconMode() { 866 return mPhone.getCdmaEriIconMode(); 867 } 868 869 /** 870 * Returns the CDMA ERI text, 871 */ 872 public String getCdmaEriText() { 873 return mPhone.getCdmaEriText(); 874 } 875 876 /** 877 * Returns true if CDMA provisioning needs to run. 878 */ 879 public boolean needsOtaServiceProvisioning() { 880 return mPhone.needsOtaServiceProvisioning(); 881 } 882 883 /** 884 * Returns the unread count of voicemails 885 */ 886 public int getVoiceMessageCount() { 887 return mPhone.getVoiceMessageCount(); 888 } 889 890 /** 891 * Returns the data network type 892 * 893 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}. 894 */ 895 @Override 896 public int getNetworkType() { 897 return mPhone.getServiceState().getDataNetworkType(); 898 } 899 900 /** 901 * Returns the data network type 902 */ 903 @Override 904 public int getDataNetworkType() { 905 return mPhone.getServiceState().getDataNetworkType(); 906 } 907 908 /** 909 * Returns the data network type 910 */ 911 @Override 912 public int getVoiceNetworkType() { 913 return mPhone.getServiceState().getVoiceNetworkType(); 914 } 915 916 /** 917 * @return true if a ICC card is present 918 */ 919 public boolean hasIccCard() { 920 return mPhone.getIccCard().hasIccCard(); 921 } 922 923 /** 924 * Return if the current radio is LTE on CDMA. This 925 * is a tri-state return value as for a period of time 926 * the mode may be unknown. 927 * 928 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE} 929 * or {@link PHone#LTE_ON_CDMA_TRUE} 930 */ 931 public int getLteOnCdmaMode() { 932 return mPhone.getLteOnCdmaMode(); 933 } 934 935 @Override 936 public void toggleHold() { 937 enforceModifyPermission(); 938 939 try { 940 PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall()); 941 } catch (Exception e) { 942 Log.e(LOG_TAG, "Error during toggleHold().", e); 943 } 944 } 945 946 @Override 947 public void merge() { 948 enforceModifyPermission(); 949 950 try { 951 if (PhoneUtils.okToMergeCalls(mCM)) { 952 PhoneUtils.mergeCalls(mCM); 953 } 954 } catch (Exception e) { 955 Log.e(LOG_TAG, "Error during merge().", e); 956 } 957 } 958 959 @Override 960 public void swap() { 961 enforceModifyPermission(); 962 963 try { 964 PhoneUtils.swap(); 965 } catch (Exception e) { 966 Log.e(LOG_TAG, "Error during swap().", e); 967 } 968 } 969 970 @Override 971 public void mute(boolean onOff) { 972 enforceModifyPermission(); 973 974 try { 975 PhoneUtils.setMute(onOff); 976 } catch (Exception e) { 977 Log.e(LOG_TAG, "Error during mute().", e); 978 } 979 } 980 981 @Override 982 public void playDtmfTone(char digit, boolean timedShortTone) { 983 enforceModifyPermission(); 984 985 synchronized (mDtmfStopHandler) { 986 try { 987 mDtmfTonePlayer.playDtmfTone(digit, timedShortTone); 988 } catch (Exception e) { 989 Log.e(LOG_TAG, "Error playing DTMF tone.", e); 990 } 991 992 if (mDtmfStopRunnable != null) { 993 mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable); 994 } 995 mDtmfStopRunnable = new Runnable() { 996 @Override 997 public void run() { 998 synchronized (mDtmfStopHandler) { 999 if (mDtmfStopRunnable == this) { 1000 mDtmfTonePlayer.stopDtmfTone(); 1001 mDtmfStopRunnable = null; 1002 } 1003 } 1004 } 1005 }; 1006 mDtmfStopHandler.postDelayed(mDtmfStopRunnable, 5000); 1007 } 1008 } 1009 1010 @Override 1011 public void stopDtmfTone() { 1012 enforceModifyPermission(); 1013 1014 synchronized (mDtmfStopHandler) { 1015 try { 1016 mDtmfTonePlayer.stopDtmfTone(); 1017 } catch (Exception e) { 1018 Log.e(LOG_TAG, "Error stopping DTMF tone.", e); 1019 } 1020 1021 if (mDtmfStopRunnable != null) { 1022 mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable); 1023 mDtmfStopRunnable = null; 1024 } 1025 } 1026 } 1027 1028 @Override 1029 public void addListener(ITelephonyListener listener) { 1030 enforcePrivilegedPhoneStatePermission(); 1031 1032 if (listener == null) { 1033 throw new IllegalArgumentException("Listener must not be null."); 1034 } 1035 1036 synchronized (mListeners) { 1037 IBinder listenerBinder = listener.asBinder(); 1038 for (ITelephonyListener l : mListeners) { 1039 if (l.asBinder().equals(listenerBinder)) { 1040 Log.w(LOG_TAG, "Listener already registered. Ignoring."); 1041 return; 1042 } 1043 } 1044 mListeners.add(listener); 1045 mDeathRecipients.put(listener.asBinder(), 1046 new TelephonyListenerDeathRecipient(listener.asBinder())); 1047 1048 // update the new listener so they get the full call state immediately 1049 for (Call call : mCallModeler.getFullList()) { 1050 try { 1051 notifyListenerOfCallLocked(call, listener); 1052 } catch (RemoteException e) { 1053 Log.e(LOG_TAG, "Error updating new listener. Ignoring."); 1054 removeListenerInternal(listener); 1055 } 1056 } 1057 } 1058 } 1059 1060 @Override 1061 public void removeListener(ITelephonyListener listener) { 1062 enforcePrivilegedPhoneStatePermission(); 1063 1064 if (listener == null) { 1065 throw new IllegalArgumentException("Listener must not be null."); 1066 } 1067 1068 removeListenerInternal(listener); 1069 } 1070 1071 private void removeListenerInternal(ITelephonyListener listener) { 1072 IBinder listenerBinder = listener.asBinder(); 1073 1074 synchronized (mListeners) { 1075 for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) { 1076 ITelephonyListener nextListener = it.next(); 1077 if (nextListener.asBinder().equals(listenerBinder)) { 1078 TelephonyListenerDeathRecipient dr = mDeathRecipients.get(listener.asBinder()); 1079 if (dr != null) { 1080 dr.unlinkDeathRecipient(); 1081 } 1082 it.remove(); 1083 } 1084 } 1085 } 1086 } 1087 1088 /** CallModeler.Listener implementation **/ 1089 1090 @Override 1091 public void onDisconnect(Call call) { 1092 notifyListenersOfCall(call); 1093 } 1094 1095 @Override 1096 public void onIncoming(Call call) { 1097 notifyListenersOfCall(call); 1098 } 1099 1100 @Override 1101 public void onUpdate(List<Call> calls) { 1102 for (Call call : calls) { 1103 notifyListenersOfCall(call); 1104 } 1105 } 1106 1107 @Override 1108 public void onPostDialAction( 1109 Connection.PostDialState state, int callId, String remainingChars, char c) { } 1110 1111 private void notifyListenersOfCall(Call call) { 1112 synchronized (mListeners) { 1113 for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) { 1114 ITelephonyListener listener = it.next(); 1115 try { 1116 notifyListenerOfCallLocked(call, listener); 1117 } catch (RemoteException e) { 1118 TelephonyListenerDeathRecipient deathRecipient = 1119 mDeathRecipients.get(listener.asBinder()); 1120 if (deathRecipient != null) { 1121 deathRecipient.unlinkDeathRecipient(); 1122 } 1123 it.remove(); 1124 } 1125 } 1126 } 1127 } 1128 1129 private void notifyListenerOfCallLocked(final Call call,final ITelephonyListener listener) 1130 throws RemoteException { 1131 if (Binder.isProxy(listener)) { 1132 listener.onUpdate(call.getCallId(), call.getState(), call.getNumber()); 1133 } else { 1134 mMainThreadHandler.post(new Runnable() { 1135 1136 @Override 1137 public void run() { 1138 try { 1139 listener.onUpdate(call.getCallId(), call.getState(), call.getNumber()); 1140 } catch (RemoteException e) { 1141 Log.wtf(LOG_TAG, "Local binder call failed with RemoteException.", e); 1142 } 1143 } 1144 }); 1145 } 1146 1147 } 1148 1149 private class TelephonyListenerDeathRecipient implements Binder.DeathRecipient { 1150 private final IBinder mBinder; 1151 1152 public TelephonyListenerDeathRecipient(IBinder listener) { 1153 mBinder = listener; 1154 try { 1155 mBinder.linkToDeath(this, 0); 1156 } catch (RemoteException e) { 1157 unlinkDeathRecipient(); 1158 } 1159 } 1160 1161 @Override 1162 public void binderDied() { 1163 synchronized (mListeners) { 1164 if (mListeners.contains(mBinder)) { 1165 mListeners.remove(mBinder); 1166 Log.w(LOG_TAG, "ITelephonyListener died. Removing."); 1167 } else { 1168 Log.w(LOG_TAG, "TelephonyListener binder died but the listener " + 1169 "is not registered."); 1170 } 1171 } 1172 } 1173 1174 public void unlinkDeathRecipient() { 1175 mBinder.unlinkToDeath(this, 0); 1176 } 1177 } 1178 } 1179