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