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