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