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