blob: 3234aee7436f289ab7236bf3b92c8ecb1b03a992 [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;
Yorke Leeceb96c92014-06-11 16:34:44 -070022import android.content.res.Resources;
Santos Cordon46592f02014-07-07 15:11:35 -070023import android.os.Binder;
Santos Cordonb64c1502014-05-21 21:21:49 -070024import android.os.Handler;
Santos Cordon23baed32014-06-27 14:45:39 -070025import android.os.Looper;
Santos Cordonb64c1502014-05-21 21:21:49 -070026import android.os.Message;
27import android.os.ServiceManager;
Santos Cordon46592f02014-07-07 15:11:35 -070028import android.phone.PhoneManager;
Santos Cordon23baed32014-06-27 14:45:39 -070029import android.telecomm.CallState;
Evan Charlton94d01622014-07-20 12:32:05 -070030import android.telecomm.PhoneAccount;
Evan Charlton89176372014-07-19 18:23:09 -070031import android.telecomm.PhoneAccountHandle;
Santos Cordon46592f02014-07-07 15:11:35 -070032import android.telecomm.TelecommManager;
33import android.telephony.TelephonyManager;
34
35import com.android.internal.telecomm.ITelecommService;
Santos Cordonb64c1502014-05-21 21:21:49 -070036
Ihab Awad60509462014-06-14 16:43:08 -070037import java.util.List;
Santos Cordonb64c1502014-05-21 21:21:49 -070038
39/**
40 * Implementation of the ITelecomm interface.
41 */
42public class TelecommServiceImpl extends ITelecommService.Stub {
Santos Cordon23baed32014-06-27 14:45:39 -070043 /**
44 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
45 * request after sending. The main thread will notify the request when it is complete.
46 */
47 private static final class MainThreadRequest {
48 /** The result of the request that is run on the main thread */
49 public Object result;
50 }
Santos Cordonb64c1502014-05-21 21:21:49 -070051
52 /**
53 * A handler that processes messages on the main thread in the phone process. Since many
54 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
55 * inbound binder threads to the main thread in the phone process.
56 */
Santos Cordon23baed32014-06-27 14:45:39 -070057 private final class MainThreadHandler extends Handler {
Santos Cordonb64c1502014-05-21 21:21:49 -070058 @Override
59 public void handleMessage(Message msg) {
Santos Cordon23baed32014-06-27 14:45:39 -070060 if (msg.obj instanceof MainThreadRequest) {
61 MainThreadRequest request = (MainThreadRequest) msg.obj;
62 Object result = null;
63 switch (msg.what) {
64 case MSG_SILENCE_RINGER:
65 mCallsManager.getRinger().silence();
66 break;
67 case MSG_SHOW_CALL_SCREEN:
68 mCallsManager.getInCallController().bringToForeground(msg.arg1 == 1);
69 break;
Santos Cordon23baed32014-06-27 14:45:39 -070070 case MSG_END_CALL:
71 result = endCallInternal();
72 break;
73 case MSG_ACCEPT_RINGING_CALL:
74 acceptRingingCallInternal();
75 break;
Santos Cordon64c7e962014-07-02 15:15:27 -070076 case MSG_CANCEL_MISSED_CALLS_NOTIFICATION:
77 mMissedCallNotifier.clearMissedCalls();
78 break;
Sailesh Nepalb88795a2014-07-15 14:53:27 -070079 case MSG_IS_TTY_SUPPORTED:
80 result = mCallsManager.isTtySupported();
81 break;
82 case MSG_GET_CURRENT_TTY_MODE:
83 result = mCallsManager.getCurrentTtyMode();
84 break;
Santos Cordon23baed32014-06-27 14:45:39 -070085 }
86
87 if (result != null) {
88 request.result = result;
89 synchronized(request) {
90 request.notifyAll();
91 }
92 }
Santos Cordonb64c1502014-05-21 21:21:49 -070093 }
94 }
Santos Cordon23baed32014-06-27 14:45:39 -070095 }
Santos Cordonb64c1502014-05-21 21:21:49 -070096
Santos Cordon7da72ef2014-06-25 15:50:22 -070097 /** Private constructor; @see init() */
Santos Cordon23baed32014-06-27 14:45:39 -070098 private static final String TAG = TelecommServiceImpl.class.getSimpleName();
99
100 private static final String SERVICE_NAME = "telecomm";
101
102 private static final int MSG_SILENCE_RINGER = 1;
103 private static final int MSG_SHOW_CALL_SCREEN = 2;
Santos Cordon626697a2014-07-10 22:36:37 -0700104 private static final int MSG_END_CALL = 3;
105 private static final int MSG_ACCEPT_RINGING_CALL = 4;
106 private static final int MSG_CANCEL_MISSED_CALLS_NOTIFICATION = 5;
Sailesh Nepalb88795a2014-07-15 14:53:27 -0700107 private static final int MSG_IS_TTY_SUPPORTED = 6;
108 private static final int MSG_GET_CURRENT_TTY_MODE = 7;
Santos Cordon23baed32014-06-27 14:45:39 -0700109
110 /** The singleton instance. */
111 private static TelecommServiceImpl sInstance;
112
Santos Cordon46592f02014-07-07 15:11:35 -0700113 private final MainThreadHandler mMainThreadHandler = new MainThreadHandler();
Santos Cordon23baed32014-06-27 14:45:39 -0700114 private final CallsManager mCallsManager = CallsManager.getInstance();
Santos Cordon64c7e962014-07-02 15:15:27 -0700115 private final MissedCallNotifier mMissedCallNotifier;
Santos Cordon176ae282014-07-14 02:02:14 -0700116 private final PhoneAccountRegistrar mPhoneAccountRegistrar;
Santos Cordon46592f02014-07-07 15:11:35 -0700117 private final AppOpsManager mAppOpsManager;
Santos Cordon23baed32014-06-27 14:45:39 -0700118
Santos Cordon176ae282014-07-14 02:02:14 -0700119 private TelecommServiceImpl(
120 MissedCallNotifier missedCallNotifier, PhoneAccountRegistrar phoneAccountRegistrar) {
Santos Cordon64c7e962014-07-02 15:15:27 -0700121 mMissedCallNotifier = missedCallNotifier;
Santos Cordon176ae282014-07-14 02:02:14 -0700122 mPhoneAccountRegistrar = phoneAccountRegistrar;
Santos Cordon46592f02014-07-07 15:11:35 -0700123 mAppOpsManager =
124 (AppOpsManager) TelecommApp.getInstance().getSystemService(Context.APP_OPS_SERVICE);
125
Santos Cordon7da72ef2014-06-25 15:50:22 -0700126 publish();
127 }
128
Santos Cordonb64c1502014-05-21 21:21:49 -0700129 /**
130 * Initialize the singleton TelecommServiceImpl instance.
131 * This is only done once, at startup, from TelecommApp.onCreate().
132 */
Santos Cordon176ae282014-07-14 02:02:14 -0700133 static TelecommServiceImpl init(
134 MissedCallNotifier missedCallNotifier, PhoneAccountRegistrar phoneAccountRegistrar) {
Santos Cordonb64c1502014-05-21 21:21:49 -0700135 synchronized (TelecommServiceImpl.class) {
136 if (sInstance == null) {
Santos Cordon176ae282014-07-14 02:02:14 -0700137 sInstance = new TelecommServiceImpl(missedCallNotifier, phoneAccountRegistrar);
Santos Cordonb64c1502014-05-21 21:21:49 -0700138 } else {
139 Log.wtf(TAG, "init() called multiple times! sInstance %s", sInstance);
140 }
141 return sInstance;
142 }
143 }
144
Santos Cordonb64c1502014-05-21 21:21:49 -0700145 //
Santos Cordon23baed32014-06-27 14:45:39 -0700146 // Implementation of the ITelecommService interface.
Santos Cordonb64c1502014-05-21 21:21:49 -0700147 //
148
Santos Cordon7da72ef2014-06-25 15:50:22 -0700149 @Override
Evan Charlton89176372014-07-19 18:23:09 -0700150 public PhoneAccountHandle getDefaultOutgoingPhoneAccount() {
Ihab Awad104f8062014-07-17 11:29:35 -0700151 try {
152 return mPhoneAccountRegistrar.getDefaultOutgoingPhoneAccount();
153 } catch (Exception e) {
154 Log.e(this, e, "getDefaultOutgoingPhoneAccount");
155 throw e;
156 }
157 }
158
159 @Override
Evan Charlton89176372014-07-19 18:23:09 -0700160 public List<PhoneAccountHandle> getEnabledPhoneAccounts() {
Ihab Awad104f8062014-07-17 11:29:35 -0700161 try {
162 return mPhoneAccountRegistrar.getEnabledPhoneAccounts();
163 } catch (Exception e) {
164 Log.e(this, e, "getEnabledPhoneAccounts");
165 throw e;
166 }
Santos Cordon7da72ef2014-06-25 15:50:22 -0700167 }
168
169 @Override
Evan Charlton94d01622014-07-20 12:32:05 -0700170 public PhoneAccount getPhoneAccount(PhoneAccountHandle accountHandle) {
Ihab Awad104f8062014-07-17 11:29:35 -0700171 try {
Evan Charlton94d01622014-07-20 12:32:05 -0700172 return mPhoneAccountRegistrar.getPhoneAccount(accountHandle);
Ihab Awad104f8062014-07-17 11:29:35 -0700173 } catch (Exception e) {
Evan Charlton94d01622014-07-20 12:32:05 -0700174 Log.e(this, e, "getPhoneAccount %s", accountHandle);
Ihab Awad104f8062014-07-17 11:29:35 -0700175 throw e;
Santos Cordon176ae282014-07-14 02:02:14 -0700176 }
Ihab Awad502e5fe2014-07-09 12:34:48 -0700177 }
178
179 @Override
Evan Charlton94d01622014-07-20 12:32:05 -0700180 public void registerPhoneAccount(PhoneAccount account) {
Ihab Awad104f8062014-07-17 11:29:35 -0700181 try {
182 enforceModifyPermissionOrCallingPackage(
Evan Charlton94d01622014-07-20 12:32:05 -0700183 account.getAccountHandle().getComponentName().getPackageName());
184 mPhoneAccountRegistrar.registerPhoneAccount(account);
Ihab Awad104f8062014-07-17 11:29:35 -0700185 } catch (Exception e) {
Evan Charlton94d01622014-07-20 12:32:05 -0700186 Log.e(this, e, "registerPhoneAccount %s", account);
Ihab Awad104f8062014-07-17 11:29:35 -0700187 throw e;
188 }
Ihab Awad502e5fe2014-07-09 12:34:48 -0700189 }
190
191 @Override
Evan Charlton94d01622014-07-20 12:32:05 -0700192 public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
Ihab Awad104f8062014-07-17 11:29:35 -0700193 try {
Evan Charlton94d01622014-07-20 12:32:05 -0700194 enforceModifyPermissionOrCallingPackage(
195 accountHandle.getComponentName().getPackageName());
196 mPhoneAccountRegistrar.unregisterPhoneAccount(accountHandle);
Ihab Awad104f8062014-07-17 11:29:35 -0700197 } catch (Exception e) {
Evan Charlton94d01622014-07-20 12:32:05 -0700198 Log.e(this, e, "unregisterPhoneAccount %s", accountHandle);
Ihab Awad104f8062014-07-17 11:29:35 -0700199 throw e;
200 }
Ihab Awad502e5fe2014-07-09 12:34:48 -0700201 }
202
203 @Override
204 public void clearAccounts(String packageName) {
Ihab Awad104f8062014-07-17 11:29:35 -0700205 try {
206 enforceModifyPermissionOrCallingPackage(packageName);
207 mPhoneAccountRegistrar.clearAccounts(packageName);
208 } catch (Exception e) {
209 Log.e(this, e, "clearAccounts %s", packageName);
210 throw e;
211 }
Santos Cordon7da72ef2014-06-25 15:50:22 -0700212 }
213
Santos Cordon23baed32014-06-27 14:45:39 -0700214 /**
Santos Cordon176ae282014-07-14 02:02:14 -0700215 * @see TelecommManager#silenceRinger
Santos Cordon23baed32014-06-27 14:45:39 -0700216 */
Santos Cordon7da72ef2014-06-25 15:50:22 -0700217 @Override
Santos Cordonb64c1502014-05-21 21:21:49 -0700218 public void silenceRinger() {
219 Log.d(this, "silenceRinger");
Santos Cordonb64c1502014-05-21 21:21:49 -0700220 enforceModifyPermission();
Santos Cordon23baed32014-06-27 14:45:39 -0700221 sendRequestAsync(MSG_SILENCE_RINGER, 0);
Santos Cordonb64c1502014-05-21 21:21:49 -0700222 }
223
Santos Cordon23baed32014-06-27 14:45:39 -0700224 /**
225 * @see TelecommManager#getDefaultPhoneApp
226 */
Santos Cordon7da72ef2014-06-25 15:50:22 -0700227 @Override
228 public ComponentName getDefaultPhoneApp() {
229 Resources resources = TelecommApp.getInstance().getResources();
230 return new ComponentName(
231 resources.getString(R.string.ui_default_package),
232 resources.getString(R.string.dialer_default_class));
233 }
234
Santos Cordon23baed32014-06-27 14:45:39 -0700235 /**
236 * @see TelecommManager#isInAPhoneCall
237 */
238 @Override
239 public boolean isInAPhoneCall() {
240 enforceReadPermission();
Santos Cordon626697a2014-07-10 22:36:37 -0700241 // Do not use sendRequest() with this method since it could cause a deadlock with
242 // audio service, which we call into from the main thread: AudioManager.setMode().
243 return mCallsManager.hasAnyCalls();
Santos Cordon23baed32014-06-27 14:45:39 -0700244 }
245
246 /**
247 * @see TelecommManager#isRinging
248 */
249 @Override
250 public boolean isRinging() {
251 enforceReadPermission();
Santos Cordon626697a2014-07-10 22:36:37 -0700252 return mCallsManager.hasRingingCall();
Santos Cordon23baed32014-06-27 14:45:39 -0700253 }
254
255 /**
256 * @see TelecommManager#endCall
257 */
258 @Override
259 public boolean endCall() {
260 enforceModifyPermission();
261 return (boolean) sendRequest(MSG_END_CALL);
262 }
263
264 /**
265 * @see TelecommManager#acceptRingingCall
266 */
267 @Override
268 public void acceptRingingCall() {
269 enforceModifyPermission();
270 sendRequestAsync(MSG_ACCEPT_RINGING_CALL, 0);
271 }
272
Santos Cordon46592f02014-07-07 15:11:35 -0700273 /**
274 * @see PhoneManager#showCallScreen
275 */
Santos Cordon23baed32014-06-27 14:45:39 -0700276 @Override
277 public void showCallScreen(boolean showDialpad) {
Santos Cordon46592f02014-07-07 15:11:35 -0700278 enforceReadPermissionOrDefaultDialer();
Santos Cordon64c7e962014-07-02 15:15:27 -0700279 sendRequestAsync(MSG_SHOW_CALL_SCREEN, showDialpad ? 1 : 0);
280 }
281
Santos Cordon46592f02014-07-07 15:11:35 -0700282 /**
283 * @see PhoneManager#cancelMissedCallsNotification
284 */
Santos Cordon64c7e962014-07-02 15:15:27 -0700285 @Override
286 public void cancelMissedCallsNotification() {
Santos Cordon46592f02014-07-07 15:11:35 -0700287 enforceModifyPermissionOrDefaultDialer();
Santos Cordon64c7e962014-07-02 15:15:27 -0700288 sendRequestAsync(MSG_CANCEL_MISSED_CALLS_NOTIFICATION, 0);
Santos Cordon23baed32014-06-27 14:45:39 -0700289 }
290
Santos Cordon46592f02014-07-07 15:11:35 -0700291 /**
292 * @see PhoneManager#handlePinMmi
293 */
294 @Override
295 public boolean handlePinMmi(String dialString) {
296 enforceModifyPermissionOrDefaultDialer();
297
298 // Switch identity so that TelephonyManager checks Telecomm's permissions instead.
299 long token = Binder.clearCallingIdentity();
300 boolean retval = getTelephonyManager().handlePinMmi(dialString);
301 Binder.restoreCallingIdentity(token);
302
303 return retval;
304 }
305
Sailesh Nepalb88795a2014-07-15 14:53:27 -0700306 /**
307 * @see TelecommManager#isTtySupported
308 */
309 @Override
310 public boolean isTtySupported() {
311 enforceReadPermission();
312 return (boolean) sendRequest(MSG_IS_TTY_SUPPORTED);
313 }
314
315 /**
316 * @see TelecommManager#getCurrentTtyMode
317 */
318 @Override
319 public int getCurrentTtyMode() {
320 enforceReadPermission();
321 return (int) sendRequest(MSG_GET_CURRENT_TTY_MODE);
322 }
323
Santos Cordon7da72ef2014-06-25 15:50:22 -0700324 //
Santos Cordon46592f02014-07-07 15:11:35 -0700325 // Supporting methods for the ITelecommService interface implementation.
Santos Cordon7da72ef2014-06-25 15:50:22 -0700326 //
327
Santos Cordon23baed32014-06-27 14:45:39 -0700328 private void acceptRingingCallInternal() {
329 Call call = mCallsManager.getFirstCallWithState(CallState.RINGING);
330 if (call != null) {
Andrew Lee38931d02014-07-16 10:17:36 -0700331 call.answer(call.getVideoState());
Santos Cordon23baed32014-06-27 14:45:39 -0700332 }
333 }
334
335 private boolean endCallInternal() {
336 // Always operate on the foreground call if one exists, otherwise get the first call in
337 // priority order by call-state.
338 Call call = mCallsManager.getForegroundCall();
339 if (call == null) {
340 call = mCallsManager.getFirstCallWithState(
341 CallState.ACTIVE,
342 CallState.DIALING,
343 CallState.RINGING,
344 CallState.ON_HOLD);
345 }
346
347 if (call != null) {
348 if (call.getState() == CallState.RINGING) {
349 call.reject(false /* rejectWithMessage */, null);
350 } else {
351 call.disconnect();
352 }
353 return true;
354 }
355
356 return false;
Santos Cordonb64c1502014-05-21 21:21:49 -0700357 }
358
359 /**
360 * Make sure the caller has the MODIFY_PHONE_STATE permission.
361 *
362 * @throws SecurityException if the caller does not have the required permission
363 */
364 private void enforceModifyPermission() {
365 TelecommApp.getInstance().enforceCallingOrSelfPermission(
366 android.Manifest.permission.MODIFY_PHONE_STATE, null);
367 }
Santos Cordonf3671a62014-05-29 21:51:53 -0700368
Santos Cordon46592f02014-07-07 15:11:35 -0700369 private void enforceModifyPermissionOrDefaultDialer() {
370 if (!isDefaultDialerCalling()) {
371 enforceModifyPermission();
372 }
373 }
374
Ihab Awad502e5fe2014-07-09 12:34:48 -0700375 private void enforceModifyPermissionOrCallingPackage(String packageName) {
376 // TODO(santoscordon): Use a new telecomm permission for this instead of reusing modify.
377 try {
378 enforceModifyPermission();
379 } catch (SecurityException e) {
380 enforceCallingPackage(packageName);
381 }
382 }
383
Santos Cordon23baed32014-06-27 14:45:39 -0700384 private void enforceReadPermission() {
385 TelecommApp.getInstance().enforceCallingOrSelfPermission(
386 android.Manifest.permission.READ_PHONE_STATE, null);
Santos Cordonf3671a62014-05-29 21:51:53 -0700387 }
Yorke Leeceb96c92014-06-11 16:34:44 -0700388
Santos Cordon46592f02014-07-07 15:11:35 -0700389 private void enforceReadPermissionOrDefaultDialer() {
390 if (!isDefaultDialerCalling()) {
391 enforceReadPermission();
392 }
393 }
394
Ihab Awad502e5fe2014-07-09 12:34:48 -0700395 private void enforceCallingPackage(String packageName) {
396 mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
397 }
398
Ihab Awad98a55602014-06-30 21:27:28 -0700399 private void showCallScreenInternal(boolean showDialpad) {
400 CallsManager.getInstance().getInCallController().bringToForeground(showDialpad);
401 }
Ihab Awad60509462014-06-14 16:43:08 -0700402
Santos Cordon46592f02014-07-07 15:11:35 -0700403 private boolean isDefaultDialerCalling() {
404 ComponentName defaultDialerComponent = getDefaultPhoneApp();
405 if (defaultDialerComponent != null) {
406 try {
407 mAppOpsManager.checkPackage(
408 Binder.getCallingUid(), defaultDialerComponent.getPackageName());
409 return true;
410 } catch (SecurityException e) {
411 Log.e(TAG, e, "Could not get default dialer.");
412 }
413 }
414 return false;
415 }
416
417 private TelephonyManager getTelephonyManager() {
418 return (TelephonyManager)
419 TelecommApp.getInstance().getSystemService(Context.TELEPHONY_SERVICE);
420 }
421
Santos Cordon7da72ef2014-06-25 15:50:22 -0700422 private void publish() {
423 Log.d(this, "publish: %s", this);
424 ServiceManager.addService(SERVICE_NAME, this);
Ihab Awad60509462014-06-14 16:43:08 -0700425 }
Santos Cordon23baed32014-06-27 14:45:39 -0700426
427 private MainThreadRequest sendRequestAsync(int command, int arg1) {
428 MainThreadRequest request = new MainThreadRequest();
429 mMainThreadHandler.obtainMessage(command, arg1, 0, request).sendToTarget();
430 return request;
431 }
432
433 /**
434 * Posts the specified command to be executed on the main thread, waits for the request to
435 * complete, and returns the result.
436 */
437 private Object sendRequest(int command) {
438 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
Sailesh Nepalbca199f2014-07-02 15:57:44 -0700439 MainThreadRequest request = new MainThreadRequest();
440 mMainThreadHandler.handleMessage(mMainThreadHandler.obtainMessage(command, request));
441 return request.result;
442 } else {
443 MainThreadRequest request = sendRequestAsync(command, 0);
Santos Cordon23baed32014-06-27 14:45:39 -0700444
Sailesh Nepalbca199f2014-07-02 15:57:44 -0700445 // Wait for the request to complete
446 synchronized (request) {
447 while (request.result == null) {
448 try {
449 request.wait();
450 } catch (InterruptedException e) {
451 // Do nothing, go back and wait until the request is complete
452 }
Santos Cordon23baed32014-06-27 14:45:39 -0700453 }
454 }
Sailesh Nepalbca199f2014-07-02 15:57:44 -0700455 return request.result;
Santos Cordon23baed32014-06-27 14:45:39 -0700456 }
Santos Cordon23baed32014-06-27 14:45:39 -0700457 }
Santos Cordonb64c1502014-05-21 21:21:49 -0700458}