TelecomServiceImpl.java revision 1c5926fc5f3c828c5b16c25a5154e5a0306ea3e7
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 = getTelephonyManager().handlePinMmi(dialString);
425        Binder.restoreCallingIdentity(token);
426
427        return retval;
428    }
429
430    /**
431     * @see android.telecom.TelecomManager#isTtySupported
432     */
433    @Override
434    public boolean isTtySupported() {
435        enforceReadPermission();
436        return (boolean) sendRequest(MSG_IS_TTY_SUPPORTED);
437    }
438
439    /**
440     * @see android.telecom.TelecomManager#getCurrentTtyMode
441     */
442    @Override
443    public int getCurrentTtyMode() {
444        enforceReadPermission();
445        return (int) sendRequest(MSG_GET_CURRENT_TTY_MODE);
446    }
447
448    /**
449     * @see android.telecom.TelecomManager#addNewIncomingCall
450     */
451    @Override
452    public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
453        if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {
454            mAppOpsManager.checkPackage(
455                    Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName());
456
457            Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
458            intent.setPackage(mContext.getPackageName());
459            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
460            intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
461            if (extras != null) {
462                intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
463            }
464
465            long token = Binder.clearCallingIdentity();
466            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
467            Binder.restoreCallingIdentity(token);
468        }
469    }
470
471    //
472    // Supporting methods for the ITelecomService interface implementation.
473    //
474
475    private void acceptRingingCallInternal() {
476        Call call = mCallsManager.getFirstCallWithState(CallState.RINGING);
477        if (call != null) {
478            call.answer(call.getVideoState());
479        }
480    }
481
482    private boolean endCallInternal() {
483        // Always operate on the foreground call if one exists, otherwise get the first call in
484        // priority order by call-state.
485        Call call = mCallsManager.getForegroundCall();
486        if (call == null) {
487            call = mCallsManager.getFirstCallWithState(
488                    CallState.ACTIVE,
489                    CallState.DIALING,
490                    CallState.RINGING,
491                    CallState.ON_HOLD);
492        }
493
494        if (call != null) {
495            if (call.getState() == CallState.RINGING) {
496                call.reject(false /* rejectWithMessage */, null);
497            } else {
498                call.disconnect();
499            }
500            return true;
501        }
502
503        return false;
504    }
505
506    private void enforcePhoneAccountModificationForPackage(String packageName) {
507        // TODO: Use a new telecomm permission for this instead of reusing modify.
508
509        int result = mContext.checkCallingOrSelfPermission(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        mContext.enforceCallingOrSelfPermission(permission, null);
558    }
559
560    private void enforceFeature(String feature) {
561        PackageManager pm = mContext.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)mContext.getSystemService(Context.TELEPHONY_SERVICE);
584    }
585
586    private MainThreadRequest sendRequestAsync(int command, int arg1) {
587        MainThreadRequest request = new MainThreadRequest();
588        mMainThreadHandler.obtainMessage(command, arg1, 0, request).sendToTarget();
589        return request;
590    }
591
592    /**
593     * Posts the specified command to be executed on the main thread, waits for the request to
594     * complete, and returns the result.
595     */
596    private Object sendRequest(int command) {
597        if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
598            MainThreadRequest request = new MainThreadRequest();
599            mMainThreadHandler.handleMessage(mMainThreadHandler.obtainMessage(command, request));
600            return request.result;
601        } else {
602            MainThreadRequest request = sendRequestAsync(command, 0);
603
604            // Wait for the request to complete
605            synchronized (request) {
606                while (request.result == null) {
607                    try {
608                        request.wait();
609                    } catch (InterruptedException e) {
610                        // Do nothing, go back and wait until the request is complete
611                    }
612                }
613            }
614            return request.result;
615        }
616    }
617
618    @Override
619    protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
620        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
621        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
622        if (mCallsManager != null) {
623            pw.println("mCallsManager: ");
624            pw.increaseIndent();
625            mCallsManager.dump(pw);
626            pw.decreaseIndent();
627        }
628    }
629}
630