Home | History | Annotate | Download | only in callfiltering
      1 /*
      2  * Copyright (C) 2016 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 
     17 package com.android.server.telecom.callfiltering;
     18 
     19 import android.Manifest;
     20 import android.content.ComponentName;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.ServiceConnection;
     24 import android.content.pm.ResolveInfo;
     25 import android.os.Binder;
     26 import android.os.IBinder;
     27 import android.os.RemoteException;
     28 import android.os.UserHandle;
     29 import android.telecom.CallScreeningService;
     30 import android.telecom.Log;
     31 import android.text.TextUtils;
     32 
     33 import com.android.internal.telecom.ICallScreeningAdapter;
     34 import com.android.internal.telecom.ICallScreeningService;
     35 import com.android.server.telecom.Call;
     36 import com.android.server.telecom.CallsManager;
     37 import com.android.server.telecom.DefaultDialerCache;
     38 import com.android.server.telecom.LogUtils;
     39 import com.android.server.telecom.ParcelableCallUtils;
     40 import com.android.server.telecom.PhoneAccountRegistrar;
     41 import com.android.server.telecom.TelecomServiceImpl;
     42 import com.android.server.telecom.TelecomSystem;
     43 
     44 import java.util.List;
     45 
     46 /**
     47  * Binds to {@link ICallScreeningService} to allow call blocking. A single instance of this class
     48  * handles a single call.
     49  */
     50 public class CallScreeningServiceFilter implements IncomingCallFilter.CallFilter {
     51     private class CallScreeningServiceConnection implements ServiceConnection {
     52         @Override
     53         public void onServiceConnected(ComponentName componentName, IBinder service) {
     54             Log.startSession("CSCR.oSC");
     55             try {
     56                 synchronized (mTelecomLock) {
     57                     Log.addEvent(mCall, LogUtils.Events.SCREENING_BOUND, componentName);
     58                     if (!mHasFinished) {
     59                         onServiceBound(ICallScreeningService.Stub.asInterface(service));
     60                     }
     61                 }
     62             } finally {
     63                 Log.endSession();
     64             }
     65         }
     66 
     67         @Override
     68         public void onServiceDisconnected(ComponentName componentName) {
     69             Log.startSession("CSCR.oSD");
     70             try {
     71                 synchronized (mTelecomLock) {
     72                     finishCallScreening();
     73                 }
     74             } finally {
     75                 Log.endSession();
     76             }
     77         }
     78     }
     79 
     80     private class CallScreeningAdapter extends ICallScreeningAdapter.Stub {
     81         @Override
     82         public void allowCall(String callId) {
     83             Log.startSession("CSCR.aC");
     84             long token = Binder.clearCallingIdentity();
     85             try {
     86                 synchronized (mTelecomLock) {
     87                     Log.d(this, "allowCall(%s)", callId);
     88                     if (mCall != null && mCall.getId().equals(callId)) {
     89                         mResult = new CallFilteringResult(
     90                                 true, // shouldAllowCall
     91                                 false, //shouldReject
     92                                 true, //shouldAddToCallLog
     93                                 true // shouldShowNotification
     94                         );
     95                     } else {
     96                         Log.w(this, "allowCall, unknown call id: %s", callId);
     97                     }
     98                     finishCallScreening();
     99                 }
    100             } finally {
    101                 Binder.restoreCallingIdentity(token);
    102                 Log.endSession();
    103             }
    104         }
    105 
    106         @Override
    107         public void disallowCall(
    108                 String callId,
    109                 boolean shouldReject,
    110                 boolean shouldAddToCallLog,
    111                 boolean shouldShowNotification) {
    112             Log.startSession("CSCR.dC");
    113             long token = Binder.clearCallingIdentity();
    114             try {
    115                 synchronized (mTelecomLock) {
    116                     Log.i(this, "disallowCall(%s), shouldReject: %b, shouldAddToCallLog: %b, "
    117                                     + "shouldShowNotification: %b", callId, shouldReject,
    118                             shouldAddToCallLog, shouldShowNotification);
    119                     if (mCall != null && mCall.getId().equals(callId)) {
    120                         mResult = new CallFilteringResult(
    121                                 false, // shouldAllowCall
    122                                 shouldReject, //shouldReject
    123                                 shouldAddToCallLog, //shouldAddToCallLog
    124                                 shouldShowNotification // shouldShowNotification
    125                         );
    126                     } else {
    127                         Log.w(this, "disallowCall, unknown call id: %s", callId);
    128                     }
    129                     finishCallScreening();
    130                 }
    131             } finally {
    132                 Binder.restoreCallingIdentity(token);
    133                 Log.endSession();
    134             }
    135         }
    136     }
    137 
    138     private final Context mContext;
    139     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
    140     private final CallsManager mCallsManager;
    141     private final DefaultDialerCache mDefaultDialerCache;
    142     private final ParcelableCallUtils.Converter mParcelableCallUtilsConverter;
    143     private final TelecomSystem.SyncRoot mTelecomLock;
    144 
    145     private Call mCall;
    146     private CallFilterResultCallback mCallback;
    147     private ICallScreeningService mService;
    148     private ServiceConnection mConnection;
    149 
    150     private boolean mHasFinished = false;
    151     private CallFilteringResult mResult = new CallFilteringResult(
    152             true, // shouldAllowCall
    153             false, //shouldReject
    154             true, //shouldAddToCallLog
    155             true // shouldShowNotification
    156     );
    157 
    158     public CallScreeningServiceFilter(
    159             Context context,
    160             CallsManager callsManager,
    161             PhoneAccountRegistrar phoneAccountRegistrar,
    162             DefaultDialerCache defaultDialerCache,
    163             ParcelableCallUtils.Converter parcelableCallUtilsConverter,
    164             TelecomSystem.SyncRoot lock) {
    165         mContext = context;
    166         mPhoneAccountRegistrar = phoneAccountRegistrar;
    167         mCallsManager = callsManager;
    168         mDefaultDialerCache = defaultDialerCache;
    169         mParcelableCallUtilsConverter = parcelableCallUtilsConverter;
    170         mTelecomLock = lock;
    171     }
    172 
    173     @Override
    174     public void startFilterLookup(Call call, CallFilterResultCallback callback) {
    175         if (mHasFinished) {
    176             Log.w(this, "Attempting to reuse CallScreeningServiceFilter. Ignoring.");
    177             return;
    178         }
    179         Log.addEvent(call, LogUtils.Events.SCREENING_SENT);
    180         mCall = call;
    181         mCallback = callback;
    182         if (!bindService()) {
    183             Log.i(this, "Could not bind to call screening service");
    184             finishCallScreening();
    185         }
    186     }
    187 
    188     private void finishCallScreening() {
    189         if (!mHasFinished) {
    190             Log.addEvent(mCall, LogUtils.Events.SCREENING_COMPLETED, mResult);
    191             mCallback.onCallFilteringComplete(mCall, mResult);
    192 
    193             if (mConnection != null) {
    194                 // We still need to call unbind even if the service disconnected.
    195                 mContext.unbindService(mConnection);
    196                 mConnection = null;
    197             }
    198             mService = null;
    199             mHasFinished = true;
    200         }
    201     }
    202 
    203     private boolean bindService() {
    204         String dialerPackage = mDefaultDialerCache
    205                 .getDefaultDialerApplication(UserHandle.USER_CURRENT);
    206         if (TextUtils.isEmpty(dialerPackage)) {
    207             Log.i(this, "Default dialer is empty. Not performing call screening.");
    208             return false;
    209         }
    210 
    211         Intent intent = new Intent(CallScreeningService.SERVICE_INTERFACE)
    212             .setPackage(dialerPackage);
    213         List<ResolveInfo> entries = mContext.getPackageManager().queryIntentServicesAsUser(
    214                 intent, 0, mCallsManager.getCurrentUserHandle().getIdentifier());
    215         if (entries.isEmpty()) {
    216             Log.i(this, "There are no call screening services installed on this device.");
    217             return false;
    218         }
    219 
    220         ResolveInfo entry = entries.get(0);
    221         if (entry.serviceInfo == null) {
    222             Log.w(this, "The call screening service has invalid service info");
    223             return false;
    224         }
    225 
    226         if (entry.serviceInfo.permission == null || !entry.serviceInfo.permission.equals(
    227                 Manifest.permission.BIND_SCREENING_SERVICE)) {
    228             Log.w(this, "CallScreeningService must require BIND_SCREENING_SERVICE permission: " +
    229                     entry.serviceInfo.packageName);
    230             return false;
    231         }
    232 
    233         ComponentName componentName =
    234                 new ComponentName(entry.serviceInfo.packageName, entry.serviceInfo.name);
    235         Log.addEvent(mCall, LogUtils.Events.BIND_SCREENING, componentName);
    236         intent.setComponent(componentName);
    237         ServiceConnection connection = new CallScreeningServiceConnection();
    238         if (mContext.bindServiceAsUser(
    239                 intent,
    240                 connection,
    241                 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
    242                 UserHandle.CURRENT)) {
    243             Log.d(this, "bindService, found service, waiting for it to connect");
    244             mConnection = connection;
    245             return true;
    246         }
    247 
    248         return false;
    249     }
    250 
    251     private void onServiceBound(ICallScreeningService service) {
    252         mService = service;
    253         try {
    254             mService.screenCall(new CallScreeningAdapter(),
    255                     mParcelableCallUtilsConverter.toParcelableCall(
    256                             mCall,
    257                             false, /* includeVideoProvider */
    258                             mPhoneAccountRegistrar));
    259         } catch (RemoteException e) {
    260             Log.e(this, e, "Failed to set the call screening adapter.");
    261             finishCallScreening();
    262         }
    263     }
    264 }
    265