1 /* 2 * Copyright (C) 2007 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.server; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.pm.PackageManager; 22 import android.net.LinkCapabilities; 23 import android.net.LinkProperties; 24 import android.os.Binder; 25 import android.os.Bundle; 26 import android.os.IBinder; 27 import android.os.RemoteException; 28 import android.telephony.CellLocation; 29 import android.telephony.PhoneStateListener; 30 import android.telephony.ServiceState; 31 import android.telephony.SignalStrength; 32 import android.telephony.TelephonyManager; 33 import android.text.TextUtils; 34 import android.util.Slog; 35 36 import java.util.ArrayList; 37 import java.io.FileDescriptor; 38 import java.io.PrintWriter; 39 import java.net.NetworkInterface; 40 41 import com.android.internal.app.IBatteryStats; 42 import com.android.internal.telephony.ITelephonyRegistry; 43 import com.android.internal.telephony.IPhoneStateListener; 44 import com.android.internal.telephony.DefaultPhoneNotifier; 45 import com.android.internal.telephony.Phone; 46 import com.android.internal.telephony.ServiceStateTracker; 47 import com.android.internal.telephony.TelephonyIntents; 48 import com.android.server.am.BatteryStatsService; 49 50 /** 51 * Since phone process can be restarted, this class provides a centralized place 52 * that applications can register and be called back from. 53 */ 54 class TelephonyRegistry extends ITelephonyRegistry.Stub { 55 private static final String TAG = "TelephonyRegistry"; 56 private static final boolean DBG = false; 57 58 private static class Record { 59 String pkgForDebug; 60 61 IBinder binder; 62 63 IPhoneStateListener callback; 64 65 int events; 66 } 67 68 private final Context mContext; 69 70 // access should be inside synchronized (mRecords) for these two fields 71 private final ArrayList<IBinder> mRemoveList = new ArrayList<IBinder>(); 72 private final ArrayList<Record> mRecords = new ArrayList<Record>(); 73 74 private final IBatteryStats mBatteryStats; 75 76 private int mCallState = TelephonyManager.CALL_STATE_IDLE; 77 78 private String mCallIncomingNumber = ""; 79 80 private ServiceState mServiceState = new ServiceState(); 81 82 private SignalStrength mSignalStrength = new SignalStrength(); 83 84 private boolean mMessageWaiting = false; 85 86 private boolean mCallForwarding = false; 87 88 private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE; 89 90 private int mDataConnectionState = TelephonyManager.DATA_UNKNOWN; 91 92 private boolean mDataConnectionPossible = false; 93 94 private String mDataConnectionReason = ""; 95 96 private String mDataConnectionApn = ""; 97 98 private ArrayList<String> mConnectedApns; 99 100 private LinkProperties mDataConnectionLinkProperties; 101 102 private LinkCapabilities mDataConnectionLinkCapabilities; 103 104 private Bundle mCellLocation = new Bundle(); 105 106 private int mDataConnectionNetworkType; 107 108 private int mOtaspMode = ServiceStateTracker.OTASP_UNKNOWN; 109 110 static final int PHONE_STATE_PERMISSION_MASK = 111 PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | 112 PhoneStateListener.LISTEN_CALL_STATE | 113 PhoneStateListener.LISTEN_DATA_ACTIVITY | 114 PhoneStateListener.LISTEN_DATA_CONNECTION_STATE | 115 PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR; 116 117 // we keep a copy of all of the state so we can send it out when folks 118 // register for it 119 // 120 // In these calls we call with the lock held. This is safe becasuse remote 121 // calls go through a oneway interface and local calls going through a 122 // handler before they get to app code. 123 124 TelephonyRegistry(Context context) { 125 CellLocation location = CellLocation.getEmpty(); 126 127 // Note that location can be null for non-phone builds like 128 // like the generic one. 129 if (location != null) { 130 location.fillInNotifierBundle(mCellLocation); 131 } 132 mContext = context; 133 mBatteryStats = BatteryStatsService.getService(); 134 mConnectedApns = new ArrayList<String>(); 135 } 136 137 public void listen(String pkgForDebug, IPhoneStateListener callback, int events, 138 boolean notifyNow) { 139 // Slog.d(TAG, "listen pkg=" + pkgForDebug + " events=0x" + 140 // Integer.toHexString(events)); 141 if (events != 0) { 142 /* Checks permission and throws Security exception */ 143 checkListenerPermission(events); 144 145 synchronized (mRecords) { 146 // register 147 Record r = null; 148 find_and_add: { 149 IBinder b = callback.asBinder(); 150 final int N = mRecords.size(); 151 for (int i = 0; i < N; i++) { 152 r = mRecords.get(i); 153 if (b == r.binder) { 154 break find_and_add; 155 } 156 } 157 r = new Record(); 158 r.binder = b; 159 r.callback = callback; 160 r.pkgForDebug = pkgForDebug; 161 mRecords.add(r); 162 } 163 int send = events & (events ^ r.events); 164 r.events = events; 165 if (notifyNow) { 166 if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { 167 try { 168 r.callback.onServiceStateChanged(new ServiceState(mServiceState)); 169 } catch (RemoteException ex) { 170 remove(r.binder); 171 } 172 } 173 if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { 174 try { 175 int gsmSignalStrength = mSignalStrength.getGsmSignalStrength(); 176 r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 177 : gsmSignalStrength)); 178 } catch (RemoteException ex) { 179 remove(r.binder); 180 } 181 } 182 if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { 183 try { 184 r.callback.onMessageWaitingIndicatorChanged(mMessageWaiting); 185 } catch (RemoteException ex) { 186 remove(r.binder); 187 } 188 } 189 if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { 190 try { 191 r.callback.onCallForwardingIndicatorChanged(mCallForwarding); 192 } catch (RemoteException ex) { 193 remove(r.binder); 194 } 195 } 196 if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { 197 try { 198 r.callback.onCellLocationChanged(new Bundle(mCellLocation)); 199 } catch (RemoteException ex) { 200 remove(r.binder); 201 } 202 } 203 if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) { 204 try { 205 r.callback.onCallStateChanged(mCallState, mCallIncomingNumber); 206 } catch (RemoteException ex) { 207 remove(r.binder); 208 } 209 } 210 if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { 211 try { 212 r.callback.onDataConnectionStateChanged(mDataConnectionState, 213 mDataConnectionNetworkType); 214 } catch (RemoteException ex) { 215 remove(r.binder); 216 } 217 } 218 if ((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { 219 try { 220 r.callback.onDataActivity(mDataActivity); 221 } catch (RemoteException ex) { 222 remove(r.binder); 223 } 224 } 225 if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { 226 try { 227 r.callback.onSignalStrengthsChanged(mSignalStrength); 228 } catch (RemoteException ex) { 229 remove(r.binder); 230 } 231 } 232 if ((events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) { 233 try { 234 r.callback.onOtaspChanged(mOtaspMode); 235 } catch (RemoteException ex) { 236 remove(r.binder); 237 } 238 } 239 } 240 } 241 } else { 242 remove(callback.asBinder()); 243 } 244 } 245 246 private void remove(IBinder binder) { 247 synchronized (mRecords) { 248 final int recordCount = mRecords.size(); 249 for (int i = 0; i < recordCount; i++) { 250 if (mRecords.get(i).binder == binder) { 251 mRecords.remove(i); 252 return; 253 } 254 } 255 } 256 } 257 258 public void notifyCallState(int state, String incomingNumber) { 259 if (!checkNotifyPermission("notifyCallState()")) { 260 return; 261 } 262 synchronized (mRecords) { 263 mCallState = state; 264 mCallIncomingNumber = incomingNumber; 265 for (Record r : mRecords) { 266 if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) { 267 try { 268 r.callback.onCallStateChanged(state, incomingNumber); 269 } catch (RemoteException ex) { 270 mRemoveList.add(r.binder); 271 } 272 } 273 } 274 handleRemoveListLocked(); 275 } 276 broadcastCallStateChanged(state, incomingNumber); 277 } 278 279 public void notifyServiceState(ServiceState state) { 280 if (!checkNotifyPermission("notifyServiceState()")){ 281 return; 282 } 283 synchronized (mRecords) { 284 mServiceState = state; 285 for (Record r : mRecords) { 286 if ((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { 287 try { 288 r.callback.onServiceStateChanged(new ServiceState(state)); 289 } catch (RemoteException ex) { 290 mRemoveList.add(r.binder); 291 } 292 } 293 } 294 handleRemoveListLocked(); 295 } 296 broadcastServiceStateChanged(state); 297 } 298 299 public void notifySignalStrength(SignalStrength signalStrength) { 300 if (!checkNotifyPermission("notifySignalStrength()")) { 301 return; 302 } 303 synchronized (mRecords) { 304 mSignalStrength = signalStrength; 305 for (Record r : mRecords) { 306 if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { 307 try { 308 r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength)); 309 } catch (RemoteException ex) { 310 mRemoveList.add(r.binder); 311 } 312 } 313 if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { 314 try { 315 int gsmSignalStrength = signalStrength.getGsmSignalStrength(); 316 r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 317 : gsmSignalStrength)); 318 } catch (RemoteException ex) { 319 mRemoveList.add(r.binder); 320 } 321 } 322 } 323 handleRemoveListLocked(); 324 } 325 broadcastSignalStrengthChanged(signalStrength); 326 } 327 328 public void notifyMessageWaitingChanged(boolean mwi) { 329 if (!checkNotifyPermission("notifyMessageWaitingChanged()")) { 330 return; 331 } 332 synchronized (mRecords) { 333 mMessageWaiting = mwi; 334 for (Record r : mRecords) { 335 if ((r.events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { 336 try { 337 r.callback.onMessageWaitingIndicatorChanged(mwi); 338 } catch (RemoteException ex) { 339 mRemoveList.add(r.binder); 340 } 341 } 342 } 343 handleRemoveListLocked(); 344 } 345 } 346 347 public void notifyCallForwardingChanged(boolean cfi) { 348 if (!checkNotifyPermission("notifyCallForwardingChanged()")) { 349 return; 350 } 351 synchronized (mRecords) { 352 mCallForwarding = cfi; 353 for (Record r : mRecords) { 354 if ((r.events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { 355 try { 356 r.callback.onCallForwardingIndicatorChanged(cfi); 357 } catch (RemoteException ex) { 358 mRemoveList.add(r.binder); 359 } 360 } 361 } 362 handleRemoveListLocked(); 363 } 364 } 365 366 public void notifyDataActivity(int state) { 367 if (!checkNotifyPermission("notifyDataActivity()" )) { 368 return; 369 } 370 synchronized (mRecords) { 371 mDataActivity = state; 372 for (Record r : mRecords) { 373 if ((r.events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { 374 try { 375 r.callback.onDataActivity(state); 376 } catch (RemoteException ex) { 377 mRemoveList.add(r.binder); 378 } 379 } 380 } 381 handleRemoveListLocked(); 382 } 383 } 384 385 public void notifyDataConnection(int state, boolean isDataConnectivityPossible, 386 String reason, String apn, String apnType, LinkProperties linkProperties, 387 LinkCapabilities linkCapabilities, int networkType, boolean roaming) { 388 if (!checkNotifyPermission("notifyDataConnection()" )) { 389 return; 390 } 391 if (DBG) { 392 Slog.i(TAG, "notifyDataConnection: state=" + state + " isDataConnectivityPossible=" 393 + isDataConnectivityPossible + " reason='" + reason 394 + "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType); 395 } 396 synchronized (mRecords) { 397 boolean modified = false; 398 if (state == TelephonyManager.DATA_CONNECTED) { 399 if (!mConnectedApns.contains(apnType)) { 400 mConnectedApns.add(apnType); 401 if (mDataConnectionState != state) { 402 mDataConnectionState = state; 403 modified = true; 404 } 405 } 406 } else { 407 if (mConnectedApns.remove(apnType)) { 408 if (mConnectedApns.isEmpty()) { 409 mDataConnectionState = state; 410 modified = true; 411 } else { 412 // leave mDataConnectionState as is and 413 // send out the new status for the APN in question. 414 } 415 } 416 } 417 mDataConnectionPossible = isDataConnectivityPossible; 418 mDataConnectionReason = reason; 419 mDataConnectionLinkProperties = linkProperties; 420 mDataConnectionLinkCapabilities = linkCapabilities; 421 if (mDataConnectionNetworkType != networkType) { 422 mDataConnectionNetworkType = networkType; 423 // need to tell registered listeners about the new network type 424 modified = true; 425 } 426 if (modified) { 427 if (DBG) { 428 Slog.d(TAG, "onDataConnectionStateChanged(" + mDataConnectionState 429 + ", " + mDataConnectionNetworkType + ")"); 430 } 431 for (Record r : mRecords) { 432 if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { 433 try { 434 r.callback.onDataConnectionStateChanged(mDataConnectionState, 435 mDataConnectionNetworkType); 436 } catch (RemoteException ex) { 437 mRemoveList.add(r.binder); 438 } 439 } 440 } 441 handleRemoveListLocked(); 442 } 443 } 444 broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn, 445 apnType, linkProperties, linkCapabilities, roaming); 446 } 447 448 public void notifyDataConnectionFailed(String reason, String apnType) { 449 if (!checkNotifyPermission("notifyDataConnectionFailed()")) { 450 return; 451 } 452 /* 453 * This is commented out because there is no onDataConnectionFailed callback 454 * in PhoneStateListener. There should be. 455 synchronized (mRecords) { 456 mDataConnectionFailedReason = reason; 457 final int N = mRecords.size(); 458 for (int i=N-1; i>=0; i--) { 459 Record r = mRecords.get(i); 460 if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_FAILED) != 0) { 461 // XXX 462 } 463 } 464 } 465 */ 466 broadcastDataConnectionFailed(reason, apnType); 467 } 468 469 public void notifyCellLocation(Bundle cellLocation) { 470 if (!checkNotifyPermission("notifyCellLocation()")) { 471 return; 472 } 473 synchronized (mRecords) { 474 mCellLocation = cellLocation; 475 for (Record r : mRecords) { 476 if ((r.events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { 477 try { 478 r.callback.onCellLocationChanged(new Bundle(cellLocation)); 479 } catch (RemoteException ex) { 480 mRemoveList.add(r.binder); 481 } 482 483 } 484 } 485 handleRemoveListLocked(); 486 } 487 } 488 489 public void notifyOtaspChanged(int otaspMode) { 490 if (!checkNotifyPermission("notifyOtaspChanged()" )) { 491 return; 492 } 493 synchronized (mRecords) { 494 mOtaspMode = otaspMode; 495 for (Record r : mRecords) { 496 if ((r.events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) { 497 try { 498 r.callback.onOtaspChanged(otaspMode); 499 } catch (RemoteException ex) { 500 mRemoveList.add(r.binder); 501 } 502 } 503 } 504 handleRemoveListLocked(); 505 } 506 } 507 508 @Override 509 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 510 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 511 != PackageManager.PERMISSION_GRANTED) { 512 pw.println("Permission Denial: can't dump telephony.registry from from pid=" 513 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); 514 return; 515 } 516 synchronized (mRecords) { 517 final int recordCount = mRecords.size(); 518 pw.println("last known state:"); 519 pw.println(" mCallState=" + mCallState); 520 pw.println(" mCallIncomingNumber=" + mCallIncomingNumber); 521 pw.println(" mServiceState=" + mServiceState); 522 pw.println(" mSignalStrength=" + mSignalStrength); 523 pw.println(" mMessageWaiting=" + mMessageWaiting); 524 pw.println(" mCallForwarding=" + mCallForwarding); 525 pw.println(" mDataActivity=" + mDataActivity); 526 pw.println(" mDataConnectionState=" + mDataConnectionState); 527 pw.println(" mDataConnectionPossible=" + mDataConnectionPossible); 528 pw.println(" mDataConnectionReason=" + mDataConnectionReason); 529 pw.println(" mDataConnectionApn=" + mDataConnectionApn); 530 pw.println(" mDataConnectionLinkProperties=" + mDataConnectionLinkProperties); 531 pw.println(" mDataConnectionLinkCapabilities=" + mDataConnectionLinkCapabilities); 532 pw.println(" mCellLocation=" + mCellLocation); 533 pw.println("registrations: count=" + recordCount); 534 for (Record r : mRecords) { 535 pw.println(" " + r.pkgForDebug + " 0x" + Integer.toHexString(r.events)); 536 } 537 } 538 } 539 540 // 541 // the legacy intent broadcasting 542 // 543 544 private void broadcastServiceStateChanged(ServiceState state) { 545 long ident = Binder.clearCallingIdentity(); 546 try { 547 mBatteryStats.notePhoneState(state.getState()); 548 } catch (RemoteException re) { 549 // Can't do much 550 } finally { 551 Binder.restoreCallingIdentity(ident); 552 } 553 554 Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED); 555 Bundle data = new Bundle(); 556 state.fillInNotifierBundle(data); 557 intent.putExtras(data); 558 mContext.sendStickyBroadcast(intent); 559 } 560 561 private void broadcastSignalStrengthChanged(SignalStrength signalStrength) { 562 long ident = Binder.clearCallingIdentity(); 563 try { 564 mBatteryStats.notePhoneSignalStrength(signalStrength); 565 } catch (RemoteException e) { 566 /* The remote entity disappeared, we can safely ignore the exception. */ 567 } finally { 568 Binder.restoreCallingIdentity(ident); 569 } 570 571 Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED); 572 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 573 Bundle data = new Bundle(); 574 signalStrength.fillInNotifierBundle(data); 575 intent.putExtras(data); 576 mContext.sendStickyBroadcast(intent); 577 } 578 579 private void broadcastCallStateChanged(int state, String incomingNumber) { 580 long ident = Binder.clearCallingIdentity(); 581 try { 582 if (state == TelephonyManager.CALL_STATE_IDLE) { 583 mBatteryStats.notePhoneOff(); 584 } else { 585 mBatteryStats.notePhoneOn(); 586 } 587 } catch (RemoteException e) { 588 /* The remote entity disappeared, we can safely ignore the exception. */ 589 } finally { 590 Binder.restoreCallingIdentity(ident); 591 } 592 593 Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 594 intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertCallState(state).toString()); 595 if (!TextUtils.isEmpty(incomingNumber)) { 596 intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber); 597 } 598 mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE); 599 } 600 601 private void broadcastDataConnectionStateChanged(int state, 602 boolean isDataConnectivityPossible, 603 String reason, String apn, String apnType, LinkProperties linkProperties, 604 LinkCapabilities linkCapabilities, boolean roaming) { 605 // Note: not reporting to the battery stats service here, because the 606 // status bar takes care of that after taking into account all of the 607 // required info. 608 Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); 609 intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertDataState(state).toString()); 610 if (!isDataConnectivityPossible) { 611 intent.putExtra(Phone.NETWORK_UNAVAILABLE_KEY, true); 612 } 613 if (reason != null) { 614 intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason); 615 } 616 if (linkProperties != null) { 617 intent.putExtra(Phone.DATA_LINK_PROPERTIES_KEY, linkProperties); 618 String iface = linkProperties.getInterfaceName(); 619 if (iface != null) { 620 intent.putExtra(Phone.DATA_IFACE_NAME_KEY, iface); 621 } 622 } 623 if (linkCapabilities != null) { 624 intent.putExtra(Phone.DATA_LINK_CAPABILITIES_KEY, linkCapabilities); 625 } 626 if (roaming) intent.putExtra(Phone.DATA_NETWORK_ROAMING_KEY, true); 627 628 intent.putExtra(Phone.DATA_APN_KEY, apn); 629 intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType); 630 mContext.sendStickyBroadcast(intent); 631 } 632 633 private void broadcastDataConnectionFailed(String reason, String apnType) { 634 Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED); 635 intent.putExtra(Phone.FAILURE_REASON_KEY, reason); 636 intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType); 637 mContext.sendStickyBroadcast(intent); 638 } 639 640 private boolean checkNotifyPermission(String method) { 641 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) 642 == PackageManager.PERMISSION_GRANTED) { 643 return true; 644 } 645 String msg = "Modify Phone State Permission Denial: " + method + " from pid=" 646 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid(); 647 if (DBG) Slog.w(TAG, msg); 648 return false; 649 } 650 651 private void checkListenerPermission(int events) { 652 if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { 653 mContext.enforceCallingOrSelfPermission( 654 android.Manifest.permission.ACCESS_COARSE_LOCATION, null); 655 656 } 657 658 if ((events & PHONE_STATE_PERMISSION_MASK) != 0) { 659 mContext.enforceCallingOrSelfPermission( 660 android.Manifest.permission.READ_PHONE_STATE, null); 661 } 662 } 663 664 private void handleRemoveListLocked() { 665 if (mRemoveList.size() > 0) { 666 for (IBinder b: mRemoveList) { 667 remove(b); 668 } 669 mRemoveList.clear(); 670 } 671 } 672 } 673