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