TelecomServiceImpl.java revision f1863270a872c18f73fc59ab581194ed203e6612
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> getEnabledPhoneAccounts() {
197        try {
198            return mPhoneAccountRegistrar.getEnabledPhoneAccounts();
199        } catch (Exception e) {
200            Log.e(this, e, "getEnabledPhoneAccounts");
201            throw e;
202        }
203    }
204
205    @Override
206    public List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(String uriScheme) {
207        try {
208            return mPhoneAccountRegistrar.getEnabledPhoneAccounts(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            // If the account is marked as enabled or has CAPABILITY_ALWAYS_ENABLED set, check to
298            // ensure the caller has modify permission.  If they do not, set the account to be
299            // disabled and remove CAPABILITY_ALWAYS_ENABLED.
300            if (account.isEnabled() ||
301                    account.hasCapabilities(PhoneAccount.CAPABILITY_ALWAYS_ENABLED)) {
302                try {
303                    enforceModifyPermission();
304                } catch (SecurityException e) {
305                    // Caller does not have modify permission, so change account to disabled by
306                    // default and remove the CAPABILITY_ALWAYS_ENABLED capability.
307                    int capabilities = account.getCapabilities() &
308                            ~PhoneAccount.CAPABILITY_ALWAYS_ENABLED;
309                    account = account.toBuilder()
310                            .setEnabled(false)
311                            .setCapabilities(capabilities)
312                            .build();
313                }
314            }
315
316            mPhoneAccountRegistrar.registerPhoneAccount(account);
317        } catch (Exception e) {
318            Log.e(this, e, "registerPhoneAccount %s", account);
319            throw e;
320        }
321    }
322
323    @Override
324    public void setPhoneAccountEnabled(PhoneAccountHandle account, boolean isEnabled) {
325        try {
326            enforceModifyPermission();
327            mPhoneAccountRegistrar.setPhoneAccountEnabled(account, isEnabled);
328        } catch (Exception e) {
329            Log.e(this, e, "setPhoneAccountEnabled %s %d", account, isEnabled ? 1 : 0);
330            throw e;
331        }
332    }
333
334    @Override
335    public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
336        try {
337            enforcePhoneAccountModificationForPackage(
338                    accountHandle.getComponentName().getPackageName());
339            mPhoneAccountRegistrar.unregisterPhoneAccount(accountHandle);
340        } catch (Exception e) {
341            Log.e(this, e, "unregisterPhoneAccount %s", accountHandle);
342            throw e;
343        }
344    }
345
346    @Override
347    public void clearAccounts(String packageName) {
348        try {
349            enforcePhoneAccountModificationForPackage(packageName);
350            mPhoneAccountRegistrar.clearAccounts(packageName);
351        } catch (Exception e) {
352            Log.e(this, e, "clearAccounts %s", packageName);
353            throw e;
354        }
355    }
356
357    /**
358     * @see android.telecom.TelecomManager#silenceRinger
359     */
360    @Override
361    public void silenceRinger() {
362        Log.d(this, "silenceRinger");
363        enforceModifyPermission();
364        sendRequestAsync(MSG_SILENCE_RINGER, 0);
365    }
366
367    /**
368     * @see android.telecom.TelecomManager#getDefaultPhoneApp
369     */
370    @Override
371    public ComponentName getDefaultPhoneApp() {
372        Resources resources = TelecomApp.getInstance().getResources();
373        return new ComponentName(
374                resources.getString(R.string.ui_default_package),
375                resources.getString(R.string.dialer_default_class));
376    }
377
378    /**
379     * @see android.telecom.TelecomManager#isInCall
380     */
381    @Override
382    public boolean isInCall() {
383        enforceReadPermission();
384        // Do not use sendRequest() with this method since it could cause a deadlock with
385        // audio service, which we call into from the main thread: AudioManager.setMode().
386        final int callState = mCallsManager.getCallState();
387        return callState == TelephonyManager.CALL_STATE_OFFHOOK
388                || callState == TelephonyManager.CALL_STATE_RINGING;
389    }
390
391    /**
392     * @see android.telecom.TelecomManager#isRinging
393     */
394    @Override
395    public boolean isRinging() {
396        enforceReadPermission();
397        return mCallsManager.getCallState() == TelephonyManager.CALL_STATE_RINGING;
398    }
399
400    /**
401     * @see TelecomManager#getCallState
402     */
403    @Override
404    public int getCallState() {
405        enforceReadPermission();
406        return mCallsManager.getCallState();
407    }
408
409    /**
410     * @see android.telecom.TelecomManager#endCall
411     */
412    @Override
413    public boolean endCall() {
414        enforceModifyPermission();
415        return (boolean) sendRequest(MSG_END_CALL);
416    }
417
418    /**
419     * @see android.telecom.TelecomManager#acceptRingingCall
420     */
421    @Override
422    public void acceptRingingCall() {
423        enforceModifyPermission();
424        sendRequestAsync(MSG_ACCEPT_RINGING_CALL, 0);
425    }
426
427    /**
428     * @see android.telecom.TelecomManager#showInCallScreen
429     */
430    @Override
431    public void showInCallScreen(boolean showDialpad) {
432        enforceReadPermissionOrDefaultDialer();
433        sendRequestAsync(MSG_SHOW_CALL_SCREEN, showDialpad ? 1 : 0);
434    }
435
436    /**
437     * @see android.telecom.TelecomManager#cancelMissedCallsNotification
438     */
439    @Override
440    public void cancelMissedCallsNotification() {
441        enforceModifyPermissionOrDefaultDialer();
442        sendRequestAsync(MSG_CANCEL_MISSED_CALLS_NOTIFICATION, 0);
443    }
444
445    /**
446     * @see android.telecom.TelecomManager#handleMmi
447     */
448    @Override
449    public boolean handlePinMmi(String dialString) {
450        enforceModifyPermissionOrDefaultDialer();
451
452        // Switch identity so that TelephonyManager checks Telecom's permissions instead.
453        long token = Binder.clearCallingIdentity();
454        boolean retval = getTelephonyManager().handlePinMmi(dialString);
455        Binder.restoreCallingIdentity(token);
456
457        return retval;
458    }
459
460    /**
461     * @see android.telecom.TelecomManager#isTtySupported
462     */
463    @Override
464    public boolean isTtySupported() {
465        enforceReadPermission();
466        return (boolean) sendRequest(MSG_IS_TTY_SUPPORTED);
467    }
468
469    /**
470     * @see android.telecom.TelecomManager#getCurrentTtyMode
471     */
472    @Override
473    public int getCurrentTtyMode() {
474        enforceReadPermission();
475        return (int) sendRequest(MSG_GET_CURRENT_TTY_MODE);
476    }
477
478    /**
479     * @see android.telecom.TelecomManager#addNewIncomingCall
480     */
481    @Override
482    public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
483        if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {
484            mAppOpsManager.checkPackage(
485                    Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName());
486
487            Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
488            intent.setPackage(TelecomApp.getInstance().getPackageName());
489            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
490            intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
491            if (extras != null) {
492                intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
493            }
494
495            long token = Binder.clearCallingIdentity();
496            TelecomApp.getInstance().startActivityAsUser(intent, UserHandle.CURRENT);
497            Binder.restoreCallingIdentity(token);
498        }
499    }
500
501    //
502    // Supporting methods for the ITelecomService interface implementation.
503    //
504
505    private void acceptRingingCallInternal() {
506        Call call = mCallsManager.getFirstCallWithState(CallState.RINGING);
507        if (call != null) {
508            call.answer(call.getVideoState());
509        }
510    }
511
512    private boolean endCallInternal() {
513        // Always operate on the foreground call if one exists, otherwise get the first call in
514        // priority order by call-state.
515        Call call = mCallsManager.getForegroundCall();
516        if (call == null) {
517            call = mCallsManager.getFirstCallWithState(
518                    CallState.ACTIVE,
519                    CallState.DIALING,
520                    CallState.RINGING,
521                    CallState.ON_HOLD);
522        }
523
524        if (call != null) {
525            if (call.getState() == CallState.RINGING) {
526                call.reject(false /* rejectWithMessage */, null);
527            } else {
528                call.disconnect();
529            }
530            return true;
531        }
532
533        return false;
534    }
535
536    private void enforcePhoneAccountModificationForPackage(String packageName) {
537        // TODO: Use a new telecomm permission for this instead of reusing modify.
538
539        int result = TelecomApp.getInstance().checkCallingOrSelfPermission(
540                Manifest.permission.MODIFY_PHONE_STATE);
541
542        // Callers with MODIFY_PHONE_STATE can use the PhoneAccount mechanism to implement
543        // built-in behavior even when PhoneAccounts are not exposed as a third-part API. They
544        // may also modify PhoneAccounts on behalf of any 'packageName'.
545
546        if (result != PackageManager.PERMISSION_GRANTED) {
547            // Other callers are only allowed to modify PhoneAccounts if the relevant system
548            // feature is enabled ...
549            enforceConnectionServiceFeature();
550            // ... and the PhoneAccounts they refer to are for their own package.
551            enforceCallingPackage(packageName);
552        }
553    }
554
555    private void enforceReadPermissionOrDefaultDialer() {
556        if (!isDefaultDialerCalling()) {
557            enforceReadPermission();
558        }
559    }
560
561    private void enforceModifyPermissionOrDefaultDialer() {
562        if (!isDefaultDialerCalling()) {
563            enforceModifyPermission();
564        }
565    }
566
567    private void enforceCallingPackage(String packageName) {
568        mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
569    }
570
571    private void enforceConnectionServiceFeature() {
572        enforceFeature(PackageManager.FEATURE_CONNECTION_SERVICE);
573    }
574
575    private void enforceRegisterProviderOrSubscriptionPermission() {
576        enforcePermission(REGISTER_PROVIDER_OR_SUBSCRIPTION);
577    }
578
579    private void enforceReadPermission() {
580        enforcePermission(Manifest.permission.READ_PHONE_STATE);
581    }
582
583    private void enforceModifyPermission() {
584        enforcePermission(Manifest.permission.MODIFY_PHONE_STATE);
585    }
586
587    private void enforcePermission(String permission) {
588        TelecomApp.getInstance().enforceCallingOrSelfPermission(permission, null);
589    }
590
591    private void enforceFeature(String feature) {
592        PackageManager pm = TelecomApp.getInstance().getPackageManager();
593        if (!pm.hasSystemFeature(feature)) {
594            throw new UnsupportedOperationException(
595                    "System does not support feature " + feature);
596        }
597    }
598
599    private boolean isDefaultDialerCalling() {
600        ComponentName defaultDialerComponent = getDefaultPhoneApp();
601        if (defaultDialerComponent != null) {
602            try {
603                mAppOpsManager.checkPackage(
604                        Binder.getCallingUid(), defaultDialerComponent.getPackageName());
605                return true;
606            } catch (SecurityException e) {
607                Log.e(TAG, e, "Could not get default dialer.");
608            }
609        }
610        return false;
611    }
612
613    private TelephonyManager getTelephonyManager() {
614        return (TelephonyManager)
615                TelecomApp.getInstance().getSystemService(Context.TELEPHONY_SERVICE);
616    }
617
618    private void publish() {
619        Log.d(this, "publish: %s", this);
620        ServiceManager.addService(SERVICE_NAME, this);
621    }
622
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     */
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