blob: e08571a5e0b226b4d81a38ead761c34bcb9d81ec [file] [log] [blame]
Santos Cordonb64c1502014-05-21 21:21:49 -07001/*
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.telecomm;
18
Santos Cordon46592f02014-07-07 15:11:35 -070019import android.app.AppOpsManager;
Yorke Leeceb96c92014-06-11 16:34:44 -070020import android.content.ComponentName;
Ihab Awad98a55602014-06-30 21:27:28 -070021import android.content.Context;
Santos Cordonfd86bd42014-07-19 14:59:04 -070022
23import android.content.Intent;
Yorke Leeceb96c92014-06-11 16:34:44 -070024import android.content.res.Resources;
Santos Cordon46592f02014-07-07 15:11:35 -070025import android.os.Binder;
Santos Cordonfd86bd42014-07-19 14:59:04 -070026import android.os.Bundle;
Santos Cordonb64c1502014-05-21 21:21:49 -070027import android.os.Handler;
Santos Cordonfd86bd42014-07-19 14:59:04 -070028import android.os.IBinder;
Santos Cordon23baed32014-06-27 14:45:39 -070029import android.os.Looper;
Santos Cordonb64c1502014-05-21 21:21:49 -070030import android.os.Message;
31import android.os.ServiceManager;
Santos Cordon46592f02014-07-07 15:11:35 -070032import android.phone.PhoneManager;
Santos Cordon23baed32014-06-27 14:45:39 -070033import android.telecomm.CallState;
Evan Charlton94d01622014-07-20 12:32:05 -070034import android.telecomm.PhoneAccount;
Evan Charlton89176372014-07-19 18:23:09 -070035import android.telecomm.PhoneAccountHandle;
Santos Cordon46592f02014-07-07 15:11:35 -070036import android.telecomm.TelecommManager;
37import android.telephony.TelephonyManager;
38
39import com.android.internal.telecomm.ITelecommService;
Santos Cordonb64c1502014-05-21 21:21:49 -070040
Ihab Awad60509462014-06-14 16:43:08 -070041import java.util.List;
Santos Cordonb64c1502014-05-21 21:21:49 -070042
43/**
44 * Implementation of the ITelecomm interface.
45 */
46public class TelecommServiceImpl extends ITelecommService.Stub {
Santos Cordonfd86bd42014-07-19 14:59:04 -070047 /** ${inheritDoc} */
48 @Override
49 public IBinder asBinder() {
50 return super.asBinder();
51 }
52
53 /**
Santos Cordon23baed32014-06-27 14:45:39 -070054 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
55 * request after sending. The main thread will notify the request when it is complete.
56 */
57 private static final class MainThreadRequest {
58 /** The result of the request that is run on the main thread */
59 public Object result;
60 }
Santos Cordonb64c1502014-05-21 21:21:49 -070061
62 /**
63 * A handler that processes messages on the main thread in the phone process. Since many
64 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
65 * inbound binder threads to the main thread in the phone process.
66 */
Santos Cordon23baed32014-06-27 14:45:39 -070067 private final class MainThreadHandler extends Handler {
Santos Cordonb64c1502014-05-21 21:21:49 -070068 @Override
69 public void handleMessage(Message msg) {
Santos Cordon23baed32014-06-27 14:45:39 -070070 if (msg.obj instanceof MainThreadRequest) {
71 MainThreadRequest request = (MainThreadRequest) msg.obj;
72 Object result = null;
73 switch (msg.what) {
74 case MSG_SILENCE_RINGER:
75 mCallsManager.getRinger().silence();
76 break;
77 case MSG_SHOW_CALL_SCREEN:
78 mCallsManager.getInCallController().bringToForeground(msg.arg1 == 1);
79 break;
Santos Cordon23baed32014-06-27 14:45:39 -070080 case MSG_END_CALL:
81 result = endCallInternal();
82 break;
83 case MSG_ACCEPT_RINGING_CALL:
84 acceptRingingCallInternal();
85 break;
Santos Cordon64c7e962014-07-02 15:15:27 -070086 case MSG_CANCEL_MISSED_CALLS_NOTIFICATION:
87 mMissedCallNotifier.clearMissedCalls();
88 break;
Sailesh Nepalb88795a2014-07-15 14:53:27 -070089 case MSG_IS_TTY_SUPPORTED:
90 result = mCallsManager.isTtySupported();
91 break;
92 case MSG_GET_CURRENT_TTY_MODE:
93 result = mCallsManager.getCurrentTtyMode();
94 break;
Santos Cordon23baed32014-06-27 14:45:39 -070095 }
96
97 if (result != null) {
98 request.result = result;
99 synchronized(request) {
100 request.notifyAll();
101 }
102 }
Santos Cordonb64c1502014-05-21 21:21:49 -0700103 }
104 }
Santos Cordon23baed32014-06-27 14:45:39 -0700105 }
Santos Cordonb64c1502014-05-21 21:21:49 -0700106
Santos Cordon7da72ef2014-06-25 15:50:22 -0700107 /** Private constructor; @see init() */
Santos Cordon23baed32014-06-27 14:45:39 -0700108 private static final String TAG = TelecommServiceImpl.class.getSimpleName();
109
110 private static final String SERVICE_NAME = "telecomm";
111
112 private static final int MSG_SILENCE_RINGER = 1;
113 private static final int MSG_SHOW_CALL_SCREEN = 2;
Santos Cordon626697a2014-07-10 22:36:37 -0700114 private static final int MSG_END_CALL = 3;
115 private static final int MSG_ACCEPT_RINGING_CALL = 4;
116 private static final int MSG_CANCEL_MISSED_CALLS_NOTIFICATION = 5;
Sailesh Nepalb88795a2014-07-15 14:53:27 -0700117 private static final int MSG_IS_TTY_SUPPORTED = 6;
118 private static final int MSG_GET_CURRENT_TTY_MODE = 7;
Santos Cordon23baed32014-06-27 14:45:39 -0700119
120 /** The singleton instance. */
121 private static TelecommServiceImpl sInstance;
122
Santos Cordon46592f02014-07-07 15:11:35 -0700123 private final MainThreadHandler mMainThreadHandler = new MainThreadHandler();
Santos Cordon23baed32014-06-27 14:45:39 -0700124 private final CallsManager mCallsManager = CallsManager.getInstance();
Santos Cordon64c7e962014-07-02 15:15:27 -0700125 private final MissedCallNotifier mMissedCallNotifier;
Santos Cordon176ae282014-07-14 02:02:14 -0700126 private final PhoneAccountRegistrar mPhoneAccountRegistrar;
Santos Cordon46592f02014-07-07 15:11:35 -0700127 private final AppOpsManager mAppOpsManager;
Santos Cordon23baed32014-06-27 14:45:39 -0700128
Santos Cordon176ae282014-07-14 02:02:14 -0700129 private TelecommServiceImpl(
130 MissedCallNotifier missedCallNotifier, PhoneAccountRegistrar phoneAccountRegistrar) {
Santos Cordon64c7e962014-07-02 15:15:27 -0700131 mMissedCallNotifier = missedCallNotifier;
Santos Cordon176ae282014-07-14 02:02:14 -0700132 mPhoneAccountRegistrar = phoneAccountRegistrar;
Santos Cordon46592f02014-07-07 15:11:35 -0700133 mAppOpsManager =
134 (AppOpsManager) TelecommApp.getInstance().getSystemService(Context.APP_OPS_SERVICE);
135
Santos Cordon7da72ef2014-06-25 15:50:22 -0700136 publish();
137 }
138
Santos Cordonb64c1502014-05-21 21:21:49 -0700139 /**
140 * Initialize the singleton TelecommServiceImpl instance.
141 * This is only done once, at startup, from TelecommApp.onCreate().
142 */
Santos Cordon176ae282014-07-14 02:02:14 -0700143 static TelecommServiceImpl init(
144 MissedCallNotifier missedCallNotifier, PhoneAccountRegistrar phoneAccountRegistrar) {
Santos Cordonb64c1502014-05-21 21:21:49 -0700145 synchronized (TelecommServiceImpl.class) {
146 if (sInstance == null) {
Santos Cordon176ae282014-07-14 02:02:14 -0700147 sInstance = new TelecommServiceImpl(missedCallNotifier, phoneAccountRegistrar);
Santos Cordonb64c1502014-05-21 21:21:49 -0700148 } else {
149 Log.wtf(TAG, "init() called multiple times! sInstance %s", sInstance);
150 }
151 return sInstance;
152 }
153 }
154
Santos Cordonb64c1502014-05-21 21:21:49 -0700155 //
Santos Cordon23baed32014-06-27 14:45:39 -0700156 // Implementation of the ITelecommService interface.
Santos Cordonb64c1502014-05-21 21:21:49 -0700157 //
158
Santos Cordon7da72ef2014-06-25 15:50:22 -0700159 @Override
Evan Charlton89176372014-07-19 18:23:09 -0700160 public PhoneAccountHandle getDefaultOutgoingPhoneAccount() {
Ihab Awad104f8062014-07-17 11:29:35 -0700161 try {
162 return mPhoneAccountRegistrar.getDefaultOutgoingPhoneAccount();
163 } catch (Exception e) {
164 Log.e(this, e, "getDefaultOutgoingPhoneAccount");
165 throw e;
166 }
167 }
168
169 @Override
Evan Charlton89176372014-07-19 18:23:09 -0700170 public List<PhoneAccountHandle> getEnabledPhoneAccounts() {
Ihab Awad104f8062014-07-17 11:29:35 -0700171 try {
172 return mPhoneAccountRegistrar.getEnabledPhoneAccounts();
173 } catch (Exception e) {
174 Log.e(this, e, "getEnabledPhoneAccounts");
175 throw e;
176 }
Santos Cordon7da72ef2014-06-25 15:50:22 -0700177 }
178
179 @Override
Evan Charlton94d01622014-07-20 12:32:05 -0700180 public PhoneAccount getPhoneAccount(PhoneAccountHandle accountHandle) {
Ihab Awad104f8062014-07-17 11:29:35 -0700181 try {
Evan Charlton94d01622014-07-20 12:32:05 -0700182 return mPhoneAccountRegistrar.getPhoneAccount(accountHandle);
Ihab Awad104f8062014-07-17 11:29:35 -0700183 } catch (Exception e) {
Evan Charlton94d01622014-07-20 12:32:05 -0700184 Log.e(this, e, "getPhoneAccount %s", accountHandle);
Ihab Awad104f8062014-07-17 11:29:35 -0700185 throw e;
Santos Cordon176ae282014-07-14 02:02:14 -0700186 }
Ihab Awad502e5fe2014-07-09 12:34:48 -0700187 }
188
189 @Override
Evan Charlton94d01622014-07-20 12:32:05 -0700190 public void registerPhoneAccount(PhoneAccount account) {
Ihab Awad104f8062014-07-17 11:29:35 -0700191 try {
192 enforceModifyPermissionOrCallingPackage(
Evan Charlton94d01622014-07-20 12:32:05 -0700193 account.getAccountHandle().getComponentName().getPackageName());
194 mPhoneAccountRegistrar.registerPhoneAccount(account);
Ihab Awad104f8062014-07-17 11:29:35 -0700195 } catch (Exception e) {
Evan Charlton94d01622014-07-20 12:32:05 -0700196 Log.e(this, e, "registerPhoneAccount %s", account);
Ihab Awad104f8062014-07-17 11:29:35 -0700197 throw e;
198 }
Ihab Awad502e5fe2014-07-09 12:34:48 -0700199 }
200
201 @Override
Evan Charlton94d01622014-07-20 12:32:05 -0700202 public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
Ihab Awad104f8062014-07-17 11:29:35 -0700203 try {
Evan Charlton94d01622014-07-20 12:32:05 -0700204 enforceModifyPermissionOrCallingPackage(
205 accountHandle.getComponentName().getPackageName());
206 mPhoneAccountRegistrar.unregisterPhoneAccount(accountHandle);
Ihab Awad104f8062014-07-17 11:29:35 -0700207 } catch (Exception e) {
Evan Charlton94d01622014-07-20 12:32:05 -0700208 Log.e(this, e, "unregisterPhoneAccount %s", accountHandle);
Ihab Awad104f8062014-07-17 11:29:35 -0700209 throw e;
210 }
Ihab Awad502e5fe2014-07-09 12:34:48 -0700211 }
212
213 @Override
214 public void clearAccounts(String packageName) {
Ihab Awad104f8062014-07-17 11:29:35 -0700215 try {
216 enforceModifyPermissionOrCallingPackage(packageName);
217 mPhoneAccountRegistrar.clearAccounts(packageName);
218 } catch (Exception e) {
219 Log.e(this, e, "clearAccounts %s", packageName);
220 throw e;
221 }
Santos Cordon7da72ef2014-06-25 15:50:22 -0700222 }
223
Santos Cordon23baed32014-06-27 14:45:39 -0700224 /**
Santos Cordon176ae282014-07-14 02:02:14 -0700225 * @see TelecommManager#silenceRinger
Santos Cordon23baed32014-06-27 14:45:39 -0700226 */
Santos Cordon7da72ef2014-06-25 15:50:22 -0700227 @Override
Santos Cordonb64c1502014-05-21 21:21:49 -0700228 public void silenceRinger() {
229 Log.d(this, "silenceRinger");
Santos Cordonb64c1502014-05-21 21:21:49 -0700230 enforceModifyPermission();
Santos Cordon23baed32014-06-27 14:45:39 -0700231 sendRequestAsync(MSG_SILENCE_RINGER, 0);
Santos Cordonb64c1502014-05-21 21:21:49 -0700232 }
233
Santos Cordon23baed32014-06-27 14:45:39 -0700234 /**
235 * @see TelecommManager#getDefaultPhoneApp
236 */
Santos Cordon7da72ef2014-06-25 15:50:22 -0700237 @Override
238 public ComponentName getDefaultPhoneApp() {
239 Resources resources = TelecommApp.getInstance().getResources();
240 return new ComponentName(
241 resources.getString(R.string.ui_default_package),
242 resources.getString(R.string.dialer_default_class));
243 }
244
Santos Cordon23baed32014-06-27 14:45:39 -0700245 /**
246 * @see TelecommManager#isInAPhoneCall
247 */
248 @Override
249 public boolean isInAPhoneCall() {
250 enforceReadPermission();
Santos Cordon626697a2014-07-10 22:36:37 -0700251 // Do not use sendRequest() with this method since it could cause a deadlock with
252 // audio service, which we call into from the main thread: AudioManager.setMode().
253 return mCallsManager.hasAnyCalls();
Santos Cordon23baed32014-06-27 14:45:39 -0700254 }
255
256 /**
257 * @see TelecommManager#isRinging
258 */
259 @Override
260 public boolean isRinging() {
261 enforceReadPermission();
Santos Cordon626697a2014-07-10 22:36:37 -0700262 return mCallsManager.hasRingingCall();
Santos Cordon23baed32014-06-27 14:45:39 -0700263 }
264
265 /**
266 * @see TelecommManager#endCall
267 */
268 @Override
269 public boolean endCall() {
270 enforceModifyPermission();
271 return (boolean) sendRequest(MSG_END_CALL);
272 }
273
274 /**
275 * @see TelecommManager#acceptRingingCall
276 */
277 @Override
278 public void acceptRingingCall() {
279 enforceModifyPermission();
280 sendRequestAsync(MSG_ACCEPT_RINGING_CALL, 0);
281 }
282
Santos Cordon46592f02014-07-07 15:11:35 -0700283 /**
284 * @see PhoneManager#showCallScreen
285 */
Santos Cordon23baed32014-06-27 14:45:39 -0700286 @Override
287 public void showCallScreen(boolean showDialpad) {
Santos Cordon46592f02014-07-07 15:11:35 -0700288 enforceReadPermissionOrDefaultDialer();
Santos Cordon64c7e962014-07-02 15:15:27 -0700289 sendRequestAsync(MSG_SHOW_CALL_SCREEN, showDialpad ? 1 : 0);
290 }
291
Santos Cordon46592f02014-07-07 15:11:35 -0700292 /**
293 * @see PhoneManager#cancelMissedCallsNotification
294 */
Santos Cordon64c7e962014-07-02 15:15:27 -0700295 @Override
296 public void cancelMissedCallsNotification() {
Santos Cordon46592f02014-07-07 15:11:35 -0700297 enforceModifyPermissionOrDefaultDialer();
Santos Cordon64c7e962014-07-02 15:15:27 -0700298 sendRequestAsync(MSG_CANCEL_MISSED_CALLS_NOTIFICATION, 0);
Santos Cordon23baed32014-06-27 14:45:39 -0700299 }
300
Santos Cordon46592f02014-07-07 15:11:35 -0700301 /**
302 * @see PhoneManager#handlePinMmi
303 */
304 @Override
305 public boolean handlePinMmi(String dialString) {
306 enforceModifyPermissionOrDefaultDialer();
307
308 // Switch identity so that TelephonyManager checks Telecomm's permissions instead.
309 long token = Binder.clearCallingIdentity();
310 boolean retval = getTelephonyManager().handlePinMmi(dialString);
311 Binder.restoreCallingIdentity(token);
312
313 return retval;
314 }
315
Sailesh Nepalb88795a2014-07-15 14:53:27 -0700316 /**
317 * @see TelecommManager#isTtySupported
318 */
319 @Override
320 public boolean isTtySupported() {
321 enforceReadPermission();
322 return (boolean) sendRequest(MSG_IS_TTY_SUPPORTED);
323 }
324
325 /**
326 * @see TelecommManager#getCurrentTtyMode
327 */
328 @Override
329 public int getCurrentTtyMode() {
330 enforceReadPermission();
331 return (int) sendRequest(MSG_GET_CURRENT_TTY_MODE);
332 }
333
Santos Cordonfd86bd42014-07-19 14:59:04 -0700334 /**
335 * @see TelecommManager#addNewIncomingCall
336 */
337 @Override
338 public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
339 if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {
340 mAppOpsManager.checkPackage(
341 Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName());
342
343 Intent intent = new Intent(TelecommManager.ACTION_INCOMING_CALL);
344 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
345 intent.putExtra(TelecommManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
346 if (extras != null) {
347 intent.putExtra(TelecommManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
348 }
349
350 long token = Binder.clearCallingIdentity();
351 TelecommApp.getInstance().startActivity(intent);
352 Binder.restoreCallingIdentity(token);
353 }
354 }
355
Santos Cordon7da72ef2014-06-25 15:50:22 -0700356 //
Santos Cordon46592f02014-07-07 15:11:35 -0700357 // Supporting methods for the ITelecommService interface implementation.
Santos Cordon7da72ef2014-06-25 15:50:22 -0700358 //
359
Santos Cordon23baed32014-06-27 14:45:39 -0700360 private void acceptRingingCallInternal() {
361 Call call = mCallsManager.getFirstCallWithState(CallState.RINGING);
362 if (call != null) {
Andrew Lee38931d02014-07-16 10:17:36 -0700363 call.answer(call.getVideoState());
Santos Cordon23baed32014-06-27 14:45:39 -0700364 }
365 }
366
367 private boolean endCallInternal() {
368 // Always operate on the foreground call if one exists, otherwise get the first call in
369 // priority order by call-state.
370 Call call = mCallsManager.getForegroundCall();
371 if (call == null) {
372 call = mCallsManager.getFirstCallWithState(
373 CallState.ACTIVE,
374 CallState.DIALING,
375 CallState.RINGING,
376 CallState.ON_HOLD);
377 }
378
379 if (call != null) {
380 if (call.getState() == CallState.RINGING) {
381 call.reject(false /* rejectWithMessage */, null);
382 } else {
383 call.disconnect();
384 }
385 return true;
386 }
387
388 return false;
Santos Cordonb64c1502014-05-21 21:21:49 -0700389 }
390
391 /**
392 * Make sure the caller has the MODIFY_PHONE_STATE permission.
393 *
394 * @throws SecurityException if the caller does not have the required permission
395 */
396 private void enforceModifyPermission() {
397 TelecommApp.getInstance().enforceCallingOrSelfPermission(
398 android.Manifest.permission.MODIFY_PHONE_STATE, null);
399 }
Santos Cordonf3671a62014-05-29 21:51:53 -0700400
Santos Cordon46592f02014-07-07 15:11:35 -0700401 private void enforceModifyPermissionOrDefaultDialer() {
402 if (!isDefaultDialerCalling()) {
403 enforceModifyPermission();
404 }
405 }
406
Ihab Awad502e5fe2014-07-09 12:34:48 -0700407 private void enforceModifyPermissionOrCallingPackage(String packageName) {
408 // TODO(santoscordon): Use a new telecomm permission for this instead of reusing modify.
409 try {
410 enforceModifyPermission();
411 } catch (SecurityException e) {
412 enforceCallingPackage(packageName);
413 }
414 }
415
Santos Cordon23baed32014-06-27 14:45:39 -0700416 private void enforceReadPermission() {
417 TelecommApp.getInstance().enforceCallingOrSelfPermission(
418 android.Manifest.permission.READ_PHONE_STATE, null);
Santos Cordonf3671a62014-05-29 21:51:53 -0700419 }
Yorke Leeceb96c92014-06-11 16:34:44 -0700420
Santos Cordon46592f02014-07-07 15:11:35 -0700421 private void enforceReadPermissionOrDefaultDialer() {
422 if (!isDefaultDialerCalling()) {
423 enforceReadPermission();
424 }
425 }
426
Ihab Awad502e5fe2014-07-09 12:34:48 -0700427 private void enforceCallingPackage(String packageName) {
428 mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
429 }
430
Ihab Awad98a55602014-06-30 21:27:28 -0700431 private void showCallScreenInternal(boolean showDialpad) {
432 CallsManager.getInstance().getInCallController().bringToForeground(showDialpad);
433 }
Ihab Awad60509462014-06-14 16:43:08 -0700434
Santos Cordon46592f02014-07-07 15:11:35 -0700435 private boolean isDefaultDialerCalling() {
436 ComponentName defaultDialerComponent = getDefaultPhoneApp();
437 if (defaultDialerComponent != null) {
438 try {
439 mAppOpsManager.checkPackage(
440 Binder.getCallingUid(), defaultDialerComponent.getPackageName());
441 return true;
442 } catch (SecurityException e) {
443 Log.e(TAG, e, "Could not get default dialer.");
444 }
445 }
446 return false;
447 }
448
449 private TelephonyManager getTelephonyManager() {
450 return (TelephonyManager)
451 TelecommApp.getInstance().getSystemService(Context.TELEPHONY_SERVICE);
452 }
453
Santos Cordon7da72ef2014-06-25 15:50:22 -0700454 private void publish() {
455 Log.d(this, "publish: %s", this);
456 ServiceManager.addService(SERVICE_NAME, this);
Ihab Awad60509462014-06-14 16:43:08 -0700457 }
Santos Cordon23baed32014-06-27 14:45:39 -0700458
459 private MainThreadRequest sendRequestAsync(int command, int arg1) {
460 MainThreadRequest request = new MainThreadRequest();
461 mMainThreadHandler.obtainMessage(command, arg1, 0, request).sendToTarget();
462 return request;
463 }
464
465 /**
466 * Posts the specified command to be executed on the main thread, waits for the request to
467 * complete, and returns the result.
468 */
469 private Object sendRequest(int command) {
470 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
Sailesh Nepalbca199f2014-07-02 15:57:44 -0700471 MainThreadRequest request = new MainThreadRequest();
472 mMainThreadHandler.handleMessage(mMainThreadHandler.obtainMessage(command, request));
473 return request.result;
474 } else {
475 MainThreadRequest request = sendRequestAsync(command, 0);
Santos Cordon23baed32014-06-27 14:45:39 -0700476
Sailesh Nepalbca199f2014-07-02 15:57:44 -0700477 // Wait for the request to complete
478 synchronized (request) {
479 while (request.result == null) {
480 try {
481 request.wait();
482 } catch (InterruptedException e) {
483 // Do nothing, go back and wait until the request is complete
484 }
Santos Cordon23baed32014-06-27 14:45:39 -0700485 }
486 }
Sailesh Nepalbca199f2014-07-02 15:57:44 -0700487 return request.result;
Santos Cordon23baed32014-06-27 14:45:39 -0700488 }
Santos Cordon23baed32014-06-27 14:45:39 -0700489 }
Santos Cordonb64c1502014-05-21 21:21:49 -0700490}