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