blob: f93a7a7c6495faf457173d83b845fe5e4c00a45a [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;
Ihab Awad98a55602014-06-30 21:27:28 -070030import android.telecomm.PhoneAccount;
Ihab Awad502e5fe2014-07-09 12:34:48 -070031import android.telecomm.PhoneAccountMetadata;
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
Ihab Awad502e5fe2014-07-09 12:34:48 -0700150 public List<PhoneAccount> getEnabledPhoneAccounts() {
Santos Cordon176ae282014-07-14 02:02:14 -0700151 return mPhoneAccountRegistrar.getEnabledAccounts();
Santos Cordon7da72ef2014-06-25 15:50:22 -0700152 }
153
154 @Override
Ihab Awad502e5fe2014-07-09 12:34:48 -0700155 public PhoneAccountMetadata getPhoneAccountMetadata(PhoneAccount account) {
Santos Cordon176ae282014-07-14 02:02:14 -0700156 PhoneAccount registeredAccount = mPhoneAccountRegistrar.getRegisteredAccount(account);
157 if (registeredAccount != null) {
158 return new PhoneAccountMetadata(
159 registeredAccount, 0, account.getComponentName().getPackageName(), null);
160 }
161 return null;
Ihab Awad502e5fe2014-07-09 12:34:48 -0700162 }
163
164 @Override
165 public void registerPhoneAccount(PhoneAccount account, PhoneAccountMetadata metadata) {
166 enforceModifyPermissionOrCallingPackage(account.getComponentName().getPackageName());
Santos Cordon176ae282014-07-14 02:02:14 -0700167 mPhoneAccountRegistrar.addAccount(account);
168 // TODO(santoscordon): Implement metadata
Ihab Awad502e5fe2014-07-09 12:34:48 -0700169 }
170
171 @Override
172 public void unregisterPhoneAccount(PhoneAccount account) {
173 enforceModifyPermissionOrCallingPackage(account.getComponentName().getPackageName());
Santos Cordon176ae282014-07-14 02:02:14 -0700174 mPhoneAccountRegistrar.removeAccount(account);
Ihab Awad502e5fe2014-07-09 12:34:48 -0700175 }
176
177 @Override
178 public void clearAccounts(String packageName) {
179 enforceModifyPermissionOrCallingPackage(packageName);
Santos Cordon176ae282014-07-14 02:02:14 -0700180 // TODO(santoscordon): Is this needed?
181 Log.e(TAG, null, "Unexpected method call: clearAccounts()");
Santos Cordon7da72ef2014-06-25 15:50:22 -0700182 }
183
Santos Cordon23baed32014-06-27 14:45:39 -0700184 /**
Santos Cordon176ae282014-07-14 02:02:14 -0700185 * @see TelecommManager#silenceRinger
Santos Cordon23baed32014-06-27 14:45:39 -0700186 */
Santos Cordon7da72ef2014-06-25 15:50:22 -0700187 @Override
Santos Cordonb64c1502014-05-21 21:21:49 -0700188 public void silenceRinger() {
189 Log.d(this, "silenceRinger");
Santos Cordonb64c1502014-05-21 21:21:49 -0700190 enforceModifyPermission();
Santos Cordon23baed32014-06-27 14:45:39 -0700191 sendRequestAsync(MSG_SILENCE_RINGER, 0);
Santos Cordonb64c1502014-05-21 21:21:49 -0700192 }
193
Santos Cordon23baed32014-06-27 14:45:39 -0700194 /**
195 * @see TelecommManager#getDefaultPhoneApp
196 */
Santos Cordon7da72ef2014-06-25 15:50:22 -0700197 @Override
198 public ComponentName getDefaultPhoneApp() {
199 Resources resources = TelecommApp.getInstance().getResources();
200 return new ComponentName(
201 resources.getString(R.string.ui_default_package),
202 resources.getString(R.string.dialer_default_class));
203 }
204
Santos Cordon23baed32014-06-27 14:45:39 -0700205 /**
206 * @see TelecommManager#isInAPhoneCall
207 */
208 @Override
209 public boolean isInAPhoneCall() {
210 enforceReadPermission();
Santos Cordon626697a2014-07-10 22:36:37 -0700211 // Do not use sendRequest() with this method since it could cause a deadlock with
212 // audio service, which we call into from the main thread: AudioManager.setMode().
213 return mCallsManager.hasAnyCalls();
Santos Cordon23baed32014-06-27 14:45:39 -0700214 }
215
216 /**
217 * @see TelecommManager#isRinging
218 */
219 @Override
220 public boolean isRinging() {
221 enforceReadPermission();
Santos Cordon626697a2014-07-10 22:36:37 -0700222 return mCallsManager.hasRingingCall();
Santos Cordon23baed32014-06-27 14:45:39 -0700223 }
224
225 /**
226 * @see TelecommManager#endCall
227 */
228 @Override
229 public boolean endCall() {
230 enforceModifyPermission();
231 return (boolean) sendRequest(MSG_END_CALL);
232 }
233
234 /**
235 * @see TelecommManager#acceptRingingCall
236 */
237 @Override
238 public void acceptRingingCall() {
239 enforceModifyPermission();
240 sendRequestAsync(MSG_ACCEPT_RINGING_CALL, 0);
241 }
242
Santos Cordon46592f02014-07-07 15:11:35 -0700243 /**
244 * @see PhoneManager#showCallScreen
245 */
Santos Cordon23baed32014-06-27 14:45:39 -0700246 @Override
247 public void showCallScreen(boolean showDialpad) {
Santos Cordon46592f02014-07-07 15:11:35 -0700248 enforceReadPermissionOrDefaultDialer();
Santos Cordon64c7e962014-07-02 15:15:27 -0700249 sendRequestAsync(MSG_SHOW_CALL_SCREEN, showDialpad ? 1 : 0);
250 }
251
Santos Cordon46592f02014-07-07 15:11:35 -0700252 /**
253 * @see PhoneManager#cancelMissedCallsNotification
254 */
Santos Cordon64c7e962014-07-02 15:15:27 -0700255 @Override
256 public void cancelMissedCallsNotification() {
Santos Cordon46592f02014-07-07 15:11:35 -0700257 enforceModifyPermissionOrDefaultDialer();
Santos Cordon64c7e962014-07-02 15:15:27 -0700258 sendRequestAsync(MSG_CANCEL_MISSED_CALLS_NOTIFICATION, 0);
Santos Cordon23baed32014-06-27 14:45:39 -0700259 }
260
Santos Cordon46592f02014-07-07 15:11:35 -0700261 /**
262 * @see PhoneManager#handlePinMmi
263 */
264 @Override
265 public boolean handlePinMmi(String dialString) {
266 enforceModifyPermissionOrDefaultDialer();
267
268 // Switch identity so that TelephonyManager checks Telecomm's permissions instead.
269 long token = Binder.clearCallingIdentity();
270 boolean retval = getTelephonyManager().handlePinMmi(dialString);
271 Binder.restoreCallingIdentity(token);
272
273 return retval;
274 }
275
Sailesh Nepalb88795a2014-07-15 14:53:27 -0700276 /**
277 * @see TelecommManager#isTtySupported
278 */
279 @Override
280 public boolean isTtySupported() {
281 enforceReadPermission();
282 return (boolean) sendRequest(MSG_IS_TTY_SUPPORTED);
283 }
284
285 /**
286 * @see TelecommManager#getCurrentTtyMode
287 */
288 @Override
289 public int getCurrentTtyMode() {
290 enforceReadPermission();
291 return (int) sendRequest(MSG_GET_CURRENT_TTY_MODE);
292 }
293
Santos Cordon7da72ef2014-06-25 15:50:22 -0700294 //
Santos Cordon46592f02014-07-07 15:11:35 -0700295 // Supporting methods for the ITelecommService interface implementation.
Santos Cordon7da72ef2014-06-25 15:50:22 -0700296 //
297
Santos Cordon23baed32014-06-27 14:45:39 -0700298 private void acceptRingingCallInternal() {
299 Call call = mCallsManager.getFirstCallWithState(CallState.RINGING);
300 if (call != null) {
Andrew Lee38931d02014-07-16 10:17:36 -0700301 call.answer(call.getVideoState());
Santos Cordon23baed32014-06-27 14:45:39 -0700302 }
303 }
304
305 private boolean endCallInternal() {
306 // Always operate on the foreground call if one exists, otherwise get the first call in
307 // priority order by call-state.
308 Call call = mCallsManager.getForegroundCall();
309 if (call == null) {
310 call = mCallsManager.getFirstCallWithState(
311 CallState.ACTIVE,
312 CallState.DIALING,
313 CallState.RINGING,
314 CallState.ON_HOLD);
315 }
316
317 if (call != null) {
318 if (call.getState() == CallState.RINGING) {
319 call.reject(false /* rejectWithMessage */, null);
320 } else {
321 call.disconnect();
322 }
323 return true;
324 }
325
326 return false;
Santos Cordonb64c1502014-05-21 21:21:49 -0700327 }
328
329 /**
330 * Make sure the caller has the MODIFY_PHONE_STATE permission.
331 *
332 * @throws SecurityException if the caller does not have the required permission
333 */
334 private void enforceModifyPermission() {
335 TelecommApp.getInstance().enforceCallingOrSelfPermission(
336 android.Manifest.permission.MODIFY_PHONE_STATE, null);
337 }
Santos Cordonf3671a62014-05-29 21:51:53 -0700338
Santos Cordon46592f02014-07-07 15:11:35 -0700339 private void enforceModifyPermissionOrDefaultDialer() {
340 if (!isDefaultDialerCalling()) {
341 enforceModifyPermission();
342 }
343 }
344
Ihab Awad502e5fe2014-07-09 12:34:48 -0700345 private void enforceModifyPermissionOrCallingPackage(String packageName) {
346 // TODO(santoscordon): Use a new telecomm permission for this instead of reusing modify.
347 try {
348 enforceModifyPermission();
349 } catch (SecurityException e) {
350 enforceCallingPackage(packageName);
351 }
352 }
353
Santos Cordon23baed32014-06-27 14:45:39 -0700354 private void enforceReadPermission() {
355 TelecommApp.getInstance().enforceCallingOrSelfPermission(
356 android.Manifest.permission.READ_PHONE_STATE, null);
Santos Cordonf3671a62014-05-29 21:51:53 -0700357 }
Yorke Leeceb96c92014-06-11 16:34:44 -0700358
Santos Cordon46592f02014-07-07 15:11:35 -0700359 private void enforceReadPermissionOrDefaultDialer() {
360 if (!isDefaultDialerCalling()) {
361 enforceReadPermission();
362 }
363 }
364
Ihab Awad502e5fe2014-07-09 12:34:48 -0700365 private void enforceCallingPackage(String packageName) {
366 mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
367 }
368
Ihab Awad98a55602014-06-30 21:27:28 -0700369 private void showCallScreenInternal(boolean showDialpad) {
370 CallsManager.getInstance().getInCallController().bringToForeground(showDialpad);
371 }
Ihab Awad60509462014-06-14 16:43:08 -0700372
Santos Cordon46592f02014-07-07 15:11:35 -0700373 private boolean isDefaultDialerCalling() {
374 ComponentName defaultDialerComponent = getDefaultPhoneApp();
375 if (defaultDialerComponent != null) {
376 try {
377 mAppOpsManager.checkPackage(
378 Binder.getCallingUid(), defaultDialerComponent.getPackageName());
379 return true;
380 } catch (SecurityException e) {
381 Log.e(TAG, e, "Could not get default dialer.");
382 }
383 }
384 return false;
385 }
386
387 private TelephonyManager getTelephonyManager() {
388 return (TelephonyManager)
389 TelecommApp.getInstance().getSystemService(Context.TELEPHONY_SERVICE);
390 }
391
Santos Cordon7da72ef2014-06-25 15:50:22 -0700392 private void publish() {
393 Log.d(this, "publish: %s", this);
394 ServiceManager.addService(SERVICE_NAME, this);
Ihab Awad60509462014-06-14 16:43:08 -0700395 }
Santos Cordon23baed32014-06-27 14:45:39 -0700396
397 private MainThreadRequest sendRequestAsync(int command, int arg1) {
398 MainThreadRequest request = new MainThreadRequest();
399 mMainThreadHandler.obtainMessage(command, arg1, 0, request).sendToTarget();
400 return request;
401 }
402
403 /**
404 * Posts the specified command to be executed on the main thread, waits for the request to
405 * complete, and returns the result.
406 */
407 private Object sendRequest(int command) {
408 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
Sailesh Nepalbca199f2014-07-02 15:57:44 -0700409 MainThreadRequest request = new MainThreadRequest();
410 mMainThreadHandler.handleMessage(mMainThreadHandler.obtainMessage(command, request));
411 return request.result;
412 } else {
413 MainThreadRequest request = sendRequestAsync(command, 0);
Santos Cordon23baed32014-06-27 14:45:39 -0700414
Sailesh Nepalbca199f2014-07-02 15:57:44 -0700415 // Wait for the request to complete
416 synchronized (request) {
417 while (request.result == null) {
418 try {
419 request.wait();
420 } catch (InterruptedException e) {
421 // Do nothing, go back and wait until the request is complete
422 }
Santos Cordon23baed32014-06-27 14:45:39 -0700423 }
424 }
Sailesh Nepalbca199f2014-07-02 15:57:44 -0700425 return request.result;
Santos Cordon23baed32014-06-27 14:45:39 -0700426 }
Santos Cordon23baed32014-06-27 14:45:39 -0700427 }
Santos Cordonb64c1502014-05-21 21:21:49 -0700428}