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