TelecomServiceImpl.java revision 6be5e8c2a234f60a248c6ae6f4e3803574c20e06
1/* 2 * Copyright (C) 2014 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.telecom; 18 19import android.Manifest; 20import android.app.AppOpsManager; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.content.pm.PackageManager; 25import android.content.res.Resources; 26import android.os.Binder; 27import android.os.Bundle; 28import android.os.Handler; 29import android.os.IBinder; 30import android.os.Looper; 31import android.os.Message; 32import android.os.ServiceManager; 33import android.os.UserHandle; 34import android.telecom.CallState; 35import android.telecom.PhoneAccount; 36import android.telecom.PhoneAccountHandle; 37import android.telecom.TelecomManager; 38import android.telephony.PhoneNumberUtils; 39import android.telephony.TelephonyManager; 40 41// TODO: Needed for move to system service: import com.android.internal.R; 42import com.android.internal.telecom.ITelecomService; 43import com.android.internal.util.IndentingPrintWriter; 44 45import java.io.FileDescriptor; 46import java.io.PrintWriter; 47import java.util.List; 48 49/** 50 * Implementation of the ITelecom interface. 51 */ 52public class TelecomServiceImpl extends ITelecomService.Stub { 53 /** The context. */ 54 private Context mContext; 55 56 /** ${inheritDoc} */ 57 @Override 58 public IBinder asBinder() { 59 return super.asBinder(); 60 } 61 62 /** 63 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the 64 * request after sending. The main thread will notify the request when it is complete. 65 */ 66 private static final class MainThreadRequest { 67 /** The result of the request that is run on the main thread */ 68 public Object result; 69 /** Object that can be used to store non-integer arguments */ 70 public Object arg; 71 } 72 73 /** 74 * A handler that processes messages on the main thread in the phone process. Since many 75 * of the Phone calls are not thread safe this is needed to shuttle the requests from the 76 * inbound binder threads to the main thread in the phone process. 77 */ 78 private final class MainThreadHandler extends Handler { 79 @Override 80 public void handleMessage(Message msg) { 81 if (msg.obj instanceof MainThreadRequest) { 82 MainThreadRequest request = (MainThreadRequest) msg.obj; 83 Object result = null; 84 switch (msg.what) { 85 case MSG_SILENCE_RINGER: 86 mCallsManager.getRinger().silence(); 87 break; 88 case MSG_SHOW_CALL_SCREEN: 89 mCallsManager.getInCallController().bringToForeground(msg.arg1 == 1); 90 break; 91 case MSG_END_CALL: 92 result = endCallInternal(); 93 break; 94 case MSG_ACCEPT_RINGING_CALL: 95 acceptRingingCallInternal(); 96 break; 97 case MSG_CANCEL_MISSED_CALLS_NOTIFICATION: 98 mMissedCallNotifier.clearMissedCalls(); 99 break; 100 case MSG_IS_TTY_SUPPORTED: 101 result = mCallsManager.isTtySupported(); 102 break; 103 case MSG_GET_CURRENT_TTY_MODE: 104 result = mCallsManager.getCurrentTtyMode(); 105 break; 106 case MSG_NEW_INCOMING_CALL: 107 if (request.arg == null || !(request.arg instanceof Intent)) { 108 Log.w(this, "Invalid new incoming call request"); 109 break; 110 } 111 CallReceiver.processIncomingCallIntent((Intent) request.arg); 112 break; 113 } 114 115 if (result != null) { 116 request.result = result; 117 synchronized(request) { 118 request.notifyAll(); 119 } 120 } 121 } 122 } 123 } 124 125 /** Private constructor; @see init() */ 126 private static final String TAG = TelecomServiceImpl.class.getSimpleName(); 127 128 private static final String SERVICE_NAME = "telecom"; 129 130 private static final int MSG_SILENCE_RINGER = 1; 131 private static final int MSG_SHOW_CALL_SCREEN = 2; 132 private static final int MSG_END_CALL = 3; 133 private static final int MSG_ACCEPT_RINGING_CALL = 4; 134 private static final int MSG_CANCEL_MISSED_CALLS_NOTIFICATION = 5; 135 private static final int MSG_IS_TTY_SUPPORTED = 6; 136 private static final int MSG_GET_CURRENT_TTY_MODE = 7; 137 private static final int MSG_NEW_INCOMING_CALL = 8; 138 139 /** The singleton instance. */ 140 private static TelecomServiceImpl sInstance; 141 142 private final MainThreadHandler mMainThreadHandler = new MainThreadHandler(); 143 private final CallsManager mCallsManager; 144 private final MissedCallNotifier mMissedCallNotifier; 145 private final PhoneAccountRegistrar mPhoneAccountRegistrar; 146 private final AppOpsManager mAppOpsManager; 147 148 public TelecomServiceImpl( 149 MissedCallNotifier missedCallNotifier, PhoneAccountRegistrar phoneAccountRegistrar, 150 CallsManager callsManager, Context context) { 151 mMissedCallNotifier = missedCallNotifier; 152 mPhoneAccountRegistrar = phoneAccountRegistrar; 153 mCallsManager = callsManager; 154 mContext = context; 155 mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 156 } 157 158 // 159 // Implementation of the ITelecomService interface. 160 // 161 162 @Override 163 public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) { 164 try { 165 return mPhoneAccountRegistrar.getDefaultOutgoingPhoneAccount(uriScheme); 166 } catch (Exception e) { 167 Log.e(this, e, "getDefaultOutgoingPhoneAccount"); 168 throw e; 169 } 170 } 171 172 @Override 173 public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() { 174 try { 175 return mPhoneAccountRegistrar.getUserSelectedOutgoingPhoneAccount(); 176 } catch (Exception e) { 177 Log.e(this, e, "getUserSelectedOutgoingPhoneAccount"); 178 throw e; 179 } 180 } 181 182 @Override 183 public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) { 184 enforceModifyPermission(); 185 186 try { 187 mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(accountHandle); 188 } catch (Exception e) { 189 Log.e(this, e, "setUserSelectedOutgoingPhoneAccount"); 190 throw e; 191 } 192 } 193 194 @Override 195 public List<PhoneAccountHandle> getCallCapablePhoneAccounts() { 196 try { 197 return mPhoneAccountRegistrar.getCallCapablePhoneAccounts(); 198 } catch (Exception e) { 199 Log.e(this, e, "getCallCapablePhoneAccounts"); 200 throw e; 201 } 202 } 203 204 @Override 205 public List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(String uriScheme) { 206 try { 207 return mPhoneAccountRegistrar.getCallCapablePhoneAccounts(uriScheme); 208 } catch (Exception e) { 209 Log.e(this, e, "getPhoneAccountsSupportingScheme"); 210 throw e; 211 } 212 } 213 214 @Override 215 public List<PhoneAccountHandle> getPhoneAccountsForPackage(String packageName) { 216 try { 217 return mPhoneAccountRegistrar.getPhoneAccountsForPackage(packageName); 218 } catch (Exception e) { 219 Log.e(this, e, "getPhoneAccountsForPackage"); 220 throw e; 221 } 222 } 223 224 @Override 225 public PhoneAccount getPhoneAccount(PhoneAccountHandle accountHandle) { 226 try { 227 return mPhoneAccountRegistrar.getPhoneAccount(accountHandle); 228 } catch (Exception e) { 229 Log.e(this, e, "getPhoneAccount %s", accountHandle); 230 throw e; 231 } 232 } 233 234 @Override 235 public int getAllPhoneAccountsCount() { 236 try { 237 return mPhoneAccountRegistrar.getAllPhoneAccountsCount(); 238 } catch (Exception e) { 239 Log.e(this, e, "getAllPhoneAccountsCount"); 240 throw e; 241 } 242 } 243 244 @Override 245 public List<PhoneAccount> getAllPhoneAccounts() { 246 try { 247 return mPhoneAccountRegistrar.getAllPhoneAccounts(); 248 } catch (Exception e) { 249 Log.e(this, e, "getAllPhoneAccounts"); 250 throw e; 251 } 252 } 253 254 @Override 255 public List<PhoneAccountHandle> getAllPhoneAccountHandles() { 256 try { 257 return mPhoneAccountRegistrar.getAllPhoneAccountHandles(); 258 } catch (Exception e) { 259 Log.e(this, e, "getAllPhoneAccounts"); 260 throw e; 261 } 262 } 263 264 @Override 265 public PhoneAccountHandle getSimCallManager() { 266 try { 267 return mPhoneAccountRegistrar.getSimCallManager(); 268 } catch (Exception e) { 269 Log.e(this, e, "getSimCallManager"); 270 throw e; 271 } 272 } 273 274 @Override 275 public void setSimCallManager(PhoneAccountHandle accountHandle) { 276 enforceModifyPermission(); 277 278 try { 279 mPhoneAccountRegistrar.setSimCallManager(accountHandle); 280 } catch (Exception e) { 281 Log.e(this, e, "setSimCallManager"); 282 throw e; 283 } 284 } 285 286 @Override 287 public List<PhoneAccountHandle> getSimCallManagers() { 288 try { 289 return mPhoneAccountRegistrar.getConnectionManagerPhoneAccounts(); 290 } catch (Exception e) { 291 Log.e(this, e, "getSimCallManagers"); 292 throw e; 293 } 294 } 295 296 @Override 297 public void registerPhoneAccount(PhoneAccount account) { 298 try { 299 enforcePhoneAccountModificationForPackage( 300 account.getAccountHandle().getComponentName().getPackageName()); 301 if (account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)) { 302 enforceRegisterCallProviderPermission(); 303 } 304 if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) { 305 enforceRegisterSimSubscriptionPermission(); 306 } 307 if (account.hasCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)) { 308 enforceRegisterConnectionManagerPermission(); 309 } 310 311 mPhoneAccountRegistrar.registerPhoneAccount(account); 312 } catch (Exception e) { 313 Log.e(this, e, "registerPhoneAccount %s", account); 314 throw e; 315 } 316 } 317 318 @Override 319 public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) { 320 try { 321 enforcePhoneAccountModificationForPackage( 322 accountHandle.getComponentName().getPackageName()); 323 mPhoneAccountRegistrar.unregisterPhoneAccount(accountHandle); 324 } catch (Exception e) { 325 Log.e(this, e, "unregisterPhoneAccount %s", accountHandle); 326 throw e; 327 } 328 } 329 330 @Override 331 public void clearAccounts(String packageName) { 332 try { 333 enforcePhoneAccountModificationForPackage(packageName); 334 mPhoneAccountRegistrar.clearAccounts(packageName); 335 } catch (Exception e) { 336 Log.e(this, e, "clearAccounts %s", packageName); 337 throw e; 338 } 339 } 340 341 /** 342 * @see android.telecom.TelecomManager#isVoiceMailNumber 343 */ 344 @Override 345 public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) { 346 enforceReadPermissionOrDefaultDialer(); 347 try { 348 return mPhoneAccountRegistrar.isVoiceMailNumber(accountHandle, number); 349 } catch (Exception e) { 350 Log.e(this, e, "getSubscriptionIdForPhoneAccount"); 351 throw e; 352 } 353 } 354 355 /** 356 * @see android.telecom.TelecomManager#silenceRinger 357 */ 358 @Override 359 public void silenceRinger() { 360 Log.d(this, "silenceRinger"); 361 enforceModifyPermission(); 362 sendRequestAsync(MSG_SILENCE_RINGER, 0); 363 } 364 365 /** 366 * @see android.telecom.TelecomManager#getDefaultPhoneApp 367 */ 368 @Override 369 public ComponentName getDefaultPhoneApp() { 370 Resources resources = mContext.getResources(); 371 return new ComponentName( 372 resources.getString(R.string.ui_default_package), 373 resources.getString(R.string.dialer_default_class)); 374 } 375 376 /** 377 * @see android.telecom.TelecomManager#isInCall 378 */ 379 @Override 380 public boolean isInCall() { 381 enforceReadPermission(); 382 // Do not use sendRequest() with this method since it could cause a deadlock with 383 // audio service, which we call into from the main thread: AudioManager.setMode(). 384 final int callState = mCallsManager.getCallState(); 385 return callState == TelephonyManager.CALL_STATE_OFFHOOK 386 || callState == TelephonyManager.CALL_STATE_RINGING; 387 } 388 389 /** 390 * @see android.telecom.TelecomManager#isRinging 391 */ 392 @Override 393 public boolean isRinging() { 394 enforceReadPermission(); 395 return mCallsManager.getCallState() == TelephonyManager.CALL_STATE_RINGING; 396 } 397 398 /** 399 * @see TelecomManager#getCallState 400 */ 401 @Override 402 public int getCallState() { 403 return mCallsManager.getCallState(); 404 } 405 406 /** 407 * @see android.telecom.TelecomManager#endCall 408 */ 409 @Override 410 public boolean endCall() { 411 enforceModifyPermission(); 412 return (boolean) sendRequest(MSG_END_CALL); 413 } 414 415 /** 416 * @see android.telecom.TelecomManager#acceptRingingCall 417 */ 418 @Override 419 public void acceptRingingCall() { 420 enforceModifyPermission(); 421 sendRequestAsync(MSG_ACCEPT_RINGING_CALL, 0); 422 } 423 424 /** 425 * @see android.telecom.TelecomManager#showInCallScreen 426 */ 427 @Override 428 public void showInCallScreen(boolean showDialpad) { 429 enforceReadPermissionOrDefaultDialer(); 430 sendRequestAsync(MSG_SHOW_CALL_SCREEN, showDialpad ? 1 : 0); 431 } 432 433 /** 434 * @see android.telecom.TelecomManager#cancelMissedCallsNotification 435 */ 436 @Override 437 public void cancelMissedCallsNotification() { 438 enforceModifyPermissionOrDefaultDialer(); 439 sendRequestAsync(MSG_CANCEL_MISSED_CALLS_NOTIFICATION, 0); 440 } 441 442 /** 443 * @see android.telecom.TelecomManager#handleMmi 444 */ 445 @Override 446 public boolean handlePinMmi(String dialString) { 447 enforceModifyPermissionOrDefaultDialer(); 448 449 // Switch identity so that TelephonyManager checks Telecom's permissions instead. 450 long token = Binder.clearCallingIdentity(); 451 boolean retval = false; 452 try { 453 retval = getTelephonyManager().handlePinMmi(dialString); 454 } finally { 455 Binder.restoreCallingIdentity(token); 456 } 457 458 return retval; 459 } 460 461 /** 462 * @see android.telecom.TelecomManager#isTtySupported 463 */ 464 @Override 465 public boolean isTtySupported() { 466 enforceReadPermission(); 467 return (boolean) sendRequest(MSG_IS_TTY_SUPPORTED); 468 } 469 470 /** 471 * @see android.telecom.TelecomManager#getCurrentTtyMode 472 */ 473 @Override 474 public int getCurrentTtyMode() { 475 enforceReadPermission(); 476 return (int) sendRequest(MSG_GET_CURRENT_TTY_MODE); 477 } 478 479 /** 480 * @see android.telecom.TelecomManager#addNewIncomingCall 481 */ 482 @Override 483 public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) { 484 Log.i(this, "Adding new incoming call with phoneAccountHandle %s", phoneAccountHandle); 485 if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) { 486 mAppOpsManager.checkPackage( 487 Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName()); 488 489 Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL); 490 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle); 491 intent.putExtra(CallReceiver.KEY_IS_INCOMING_CALL, true); 492 if (extras != null) { 493 intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras); 494 } 495 sendRequestAsync(MSG_NEW_INCOMING_CALL, 0, intent); 496 } else { 497 Log.w(this, "Null phoneAccountHandle. Ignoring request to add new incoming call"); 498 } 499 } 500 501 /** 502 * @see android.telecom.TelecomManager#addNewUnknownCall 503 */ 504 @Override 505 public void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) { 506 if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null && 507 TelephonyUtil.isPstnComponentName(phoneAccountHandle.getComponentName())) { 508 mAppOpsManager.checkPackage( 509 Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName()); 510 511 Intent intent = new Intent(TelecomManager.ACTION_NEW_UNKNOWN_CALL); 512 intent.setClass(mContext, CallReceiver.class); 513 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); 514 intent.putExtras(extras); 515 intent.putExtra(CallReceiver.KEY_IS_UNKNOWN_CALL, true); 516 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle); 517 mContext.sendBroadcastAsUser(intent, UserHandle.OWNER); 518 } else { 519 Log.i(this, "Null phoneAccountHandle or not initiated by Telephony. Ignoring request" 520 + " to add new unknown call."); 521 } 522 } 523 524 // 525 // Supporting methods for the ITelecomService interface implementation. 526 // 527 528 private void acceptRingingCallInternal() { 529 Call call = mCallsManager.getFirstCallWithState(CallState.RINGING); 530 if (call != null) { 531 call.answer(call.getVideoState()); 532 } 533 } 534 535 private boolean endCallInternal() { 536 // Always operate on the foreground call if one exists, otherwise get the first call in 537 // priority order by call-state. 538 Call call = mCallsManager.getForegroundCall(); 539 if (call == null) { 540 call = mCallsManager.getFirstCallWithState( 541 CallState.ACTIVE, 542 CallState.DIALING, 543 CallState.RINGING, 544 CallState.ON_HOLD); 545 } 546 547 if (call != null) { 548 if (call.getState() == CallState.RINGING) { 549 call.reject(false /* rejectWithMessage */, null); 550 } else { 551 call.disconnect(); 552 } 553 return true; 554 } 555 556 return false; 557 } 558 559 private void enforcePhoneAccountModificationForPackage(String packageName) { 560 // TODO: Use a new telecomm permission for this instead of reusing modify. 561 562 int result = mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE); 563 564 // Callers with MODIFY_PHONE_STATE can use the PhoneAccount mechanism to implement 565 // built-in behavior even when PhoneAccounts are not exposed as a third-part API. They 566 // may also modify PhoneAccounts on behalf of any 'packageName'. 567 568 if (result != PackageManager.PERMISSION_GRANTED) { 569 // Other callers are only allowed to modify PhoneAccounts if the relevant system 570 // feature is enabled ... 571 enforceConnectionServiceFeature(); 572 // ... and the PhoneAccounts they refer to are for their own package. 573 enforceCallingPackage(packageName); 574 } 575 } 576 577 private void enforceReadPermissionOrDefaultDialer() { 578 if (!isDefaultDialerCalling()) { 579 enforceReadPermission(); 580 } 581 } 582 583 private void enforceModifyPermissionOrDefaultDialer() { 584 if (!isDefaultDialerCalling()) { 585 enforceModifyPermission(); 586 } 587 } 588 589 private void enforceCallingPackage(String packageName) { 590 mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName); 591 } 592 593 private void enforceConnectionServiceFeature() { 594 enforceFeature(PackageManager.FEATURE_CONNECTION_SERVICE); 595 } 596 597 private void enforceRegisterCallProviderPermission() { 598 enforcePermission(android.Manifest.permission.REGISTER_CALL_PROVIDER); 599 } 600 601 private void enforceRegisterSimSubscriptionPermission() { 602 enforcePermission(android.Manifest.permission.REGISTER_SIM_SUBSCRIPTION); 603 } 604 605 private void enforceRegisterConnectionManagerPermission() { 606 enforcePermission(android.Manifest.permission.REGISTER_CONNECTION_MANAGER); 607 } 608 609 private void enforceReadPermission() { 610 enforcePermission(Manifest.permission.READ_PHONE_STATE); 611 } 612 613 private void enforceModifyPermission() { 614 enforcePermission(Manifest.permission.MODIFY_PHONE_STATE); 615 } 616 617 private void enforcePermission(String permission) { 618 mContext.enforceCallingOrSelfPermission(permission, null); 619 } 620 621 private void enforceFeature(String feature) { 622 PackageManager pm = mContext.getPackageManager(); 623 if (!pm.hasSystemFeature(feature)) { 624 throw new UnsupportedOperationException( 625 "System does not support feature " + feature); 626 } 627 } 628 629 private boolean isDefaultDialerCalling() { 630 ComponentName defaultDialerComponent = getDefaultPhoneApp(); 631 if (defaultDialerComponent != null) { 632 try { 633 mAppOpsManager.checkPackage( 634 Binder.getCallingUid(), defaultDialerComponent.getPackageName()); 635 return true; 636 } catch (SecurityException e) { 637 Log.e(TAG, e, "Could not get default dialer."); 638 } 639 } 640 return false; 641 } 642 643 private TelephonyManager getTelephonyManager() { 644 return (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); 645 } 646 647 private MainThreadRequest sendRequestAsync(int command, int arg1) { 648 return sendRequestAsync(command, arg1, null); 649 } 650 651 private MainThreadRequest sendRequestAsync(int command, int arg1, Object arg) { 652 MainThreadRequest request = new MainThreadRequest(); 653 request.arg = arg; 654 mMainThreadHandler.obtainMessage(command, arg1, 0, request).sendToTarget(); 655 return request; 656 } 657 658 /** 659 * Posts the specified command to be executed on the main thread, waits for the request to 660 * complete, and returns the result. 661 */ 662 private Object sendRequest(int command) { 663 if (Looper.myLooper() == mMainThreadHandler.getLooper()) { 664 MainThreadRequest request = new MainThreadRequest(); 665 mMainThreadHandler.handleMessage(mMainThreadHandler.obtainMessage(command, request)); 666 return request.result; 667 } else { 668 MainThreadRequest request = sendRequestAsync(command, 0); 669 670 // Wait for the request to complete 671 synchronized (request) { 672 while (request.result == null) { 673 try { 674 request.wait(); 675 } catch (InterruptedException e) { 676 // Do nothing, go back and wait until the request is complete 677 } 678 } 679 } 680 return request.result; 681 } 682 } 683 684 /** 685 * Dumps the current state of the TelecomService. Used when generating problem reports. 686 * 687 * @param fd The file descriptor. 688 * @param writer The print writer to dump the state to. 689 * @param args Optional dump arguments. 690 */ 691 @Override 692 protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { 693 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 694 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 695 if (mCallsManager != null) { 696 pw.println("mCallsManager: "); 697 pw.increaseIndent(); 698 mCallsManager.dump(pw); 699 pw.decreaseIndent(); 700 701 pw.println("mPhoneAccountRegistrar: "); 702 pw.increaseIndent(); 703 mPhoneAccountRegistrar.dump(pw); 704 pw.decreaseIndent(); 705 } 706 } 707} 708