• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.content.Context;
20 import android.net.Uri;
21 import android.os.Bundle;
22 import android.os.Handler;
23 import android.os.HandlerThread;
24 import android.os.UserManager;
25 import android.provider.BlockedNumberContract;
26 import android.provider.CallLog;
27 import android.telecom.CallerInfo;
28 import android.telecom.Log;
29 import android.telecom.TelecomManager;
30 
31 import com.android.server.telecom.Call;
32 import com.android.server.telecom.CallerInfoLookupHelper;
33 import com.android.server.telecom.flags.FeatureFlags;
34 import com.android.server.telecom.LogUtils;
35 import com.android.server.telecom.LoggedHandlerExecutor;
36 import com.android.server.telecom.settings.BlockedNumbersUtil;
37 
38 import java.util.concurrent.CompletableFuture;
39 import java.util.concurrent.CompletionStage;
40 
41 public class BlockCheckerFilter extends CallFilter {
42     private final Call mCall;
43     private final Context mContext;
44     private final CallerInfoLookupHelper mCallerInfoLookupHelper;
45     private final BlockCheckerAdapter mBlockCheckerAdapter;
46     private final String TAG = "BlockCheckerFilter";
47     private boolean mContactExists;
48     private HandlerThread mHandlerThread;
49     private Handler mHandler;
50     private FeatureFlags mFeatureFlags;
51 
52     public static final long CALLER_INFO_QUERY_TIMEOUT = 5000;
53 
54     /**
55      * Integer reason indicating whether a call was blocked, and if so why.
56      * @hide
57      */
58     public static final String RES_BLOCK_STATUS = "block_status";
59 
60     /**
61      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was not
62      * blocked.
63      * @hide
64      */
65     public static final int STATUS_NOT_BLOCKED = 0;
66 
67     /**
68      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
69      * because it is in the list of blocked numbers maintained by the provider.
70      * @hide
71      */
72     public static final int STATUS_BLOCKED_IN_LIST = 1;
73 
74     /**
75      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
76      * because it is from a restricted number.
77      * @hide
78      */
79     public static final int STATUS_BLOCKED_RESTRICTED = 2;
80 
81     /**
82      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
83      * because it is from an unknown number.
84      * @hide
85      */
86     public static final int STATUS_BLOCKED_UNKNOWN_NUMBER = 3;
87 
88     /**
89      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
90      * because it is from a pay phone.
91      * @hide
92      */
93     public static final int STATUS_BLOCKED_PAYPHONE = 4;
94 
95     /**
96      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
97      * because it is from a number not in the users contacts.
98      * @hide
99      */
100     public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5;
101 
102     /**
103      * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
104      * because it is from a number not available.
105      * @hide
106      */
107     public static final int STATUS_BLOCKED_UNAVAILABLE = 6;
108 
BlockCheckerFilter(Context context, Call call, CallerInfoLookupHelper callerInfoLookupHelper, BlockCheckerAdapter blockCheckerAdapter, FeatureFlags featureFlags)109     public BlockCheckerFilter(Context context, Call call,
110             CallerInfoLookupHelper callerInfoLookupHelper,
111             BlockCheckerAdapter blockCheckerAdapter, FeatureFlags featureFlags) {
112         mCall = call;
113         mContext = context;
114         mCallerInfoLookupHelper = callerInfoLookupHelper;
115         mBlockCheckerAdapter = blockCheckerAdapter;
116         mContactExists = false;
117         mHandlerThread = new HandlerThread(TAG);
118         mHandlerThread.start();
119         mHandler = new Handler(mHandlerThread.getLooper());
120         mFeatureFlags = featureFlags;
121     }
122 
123     @Override
startFilterLookup(CallFilteringResult result)124     public CompletionStage<CallFilteringResult> startFilterLookup(CallFilteringResult result) {
125         Log.addEvent(mCall, LogUtils.Events.BLOCK_CHECK_INITIATED);
126         CompletableFuture<CallFilteringResult> resultFuture = new CompletableFuture<>();
127         Bundle extras = new Bundle();
128         final Context userContext;
129         if (mFeatureFlags.telecomMainUserInBlockCheck()) {
130             userContext = mContext.createContextAsUser(mCall.getAssociatedUser(), 0);
131         } else {
132             userContext = mContext;
133         }
134         if (BlockedNumbersUtil.isEnhancedCallBlockingEnabledByPlatform(userContext)) {
135             int presentation = mCall.getHandlePresentation();
136             extras.putInt(BlockedNumberContract.EXTRA_CALL_PRESENTATION, presentation);
137             if (presentation == TelecomManager.PRESENTATION_ALLOWED) {
138                 mCallerInfoLookupHelper.startLookup(mCall.getHandle(),
139                         new CallerInfoLookupHelper.OnQueryCompleteListener() {
140                             @Override
141                             public void onCallerInfoQueryComplete(Uri handle, CallerInfo info) {
142                                 if (info != null && info.contactExists) {
143                                     mContactExists = true;
144                                 }
145                                 getBlockStatus(resultFuture, userContext);
146                             }
147 
148                             @Override
149                             public void onContactPhotoQueryComplete(Uri handle, CallerInfo info) {
150                                 // Ignore
151                             }
152                         });
153             } else {
154                 getBlockStatus(resultFuture, userContext);
155             }
156         } else {
157             getBlockStatus(resultFuture, userContext);
158         }
159         return resultFuture;
160     }
161 
getBlockStatus( CompletableFuture<CallFilteringResult> resultFuture, Context userContext)162     private void getBlockStatus(
163             CompletableFuture<CallFilteringResult> resultFuture, Context userContext) {
164         // Set presentation and if contact exists. Used in determining if the system should block
165         // the passed in number. Use default values as they would be returned if the keys didn't
166         // exist in the extras to maintain existing behavior.
167         int presentation;
168         boolean isNumberInContacts;
169         if (BlockedNumbersUtil.isEnhancedCallBlockingEnabledByPlatform(userContext)) {
170             presentation = mCall.getHandlePresentation();
171         } else {
172             presentation = 0;
173         }
174 
175         if (presentation == TelecomManager.PRESENTATION_ALLOWED) {
176             isNumberInContacts = mContactExists;
177         } else {
178             isNumberInContacts = false;
179         }
180 
181         // Set number
182         final String number = mCall.getHandle() == null ? null :
183                 mCall.getHandle().getSchemeSpecificPart();
184 
185         CompletableFuture.supplyAsync(
186                 () -> mBlockCheckerAdapter.getBlockStatus(userContext, number,
187                         presentation, isNumberInContacts),
188                 new LoggedHandlerExecutor(mHandler, "BCF.gBS", null))
189                 .thenApplyAsync((x) -> completeResult(resultFuture, x),
190                         new LoggedHandlerExecutor(mHandler, "BCF.gBS", null));
191     }
192 
completeResult(CompletableFuture<CallFilteringResult> resultFuture, int blockStatus)193     private int completeResult(CompletableFuture<CallFilteringResult> resultFuture,
194             int blockStatus) {
195         CallFilteringResult result;
196         if (blockStatus != STATUS_NOT_BLOCKED) {
197             result = new CallFilteringResult.Builder()
198                     .setShouldAllowCall(false)
199                     .setShouldReject(true)
200                     .setShouldAddToCallLog(true)
201                     .setShouldShowNotification(false)
202                     .setShouldSilence(true)
203                     .setCallBlockReason(getBlockReason(blockStatus))
204                     .setCallScreeningAppName(null)
205                     .setCallScreeningComponentName(null)
206                     .setContactExists(mContactExists)
207                     .build();
208         } else {
209             result = new CallFilteringResult.Builder()
210                     .setShouldAllowCall(true)
211                     .setShouldReject(false)
212                     .setShouldSilence(false)
213                     .setShouldAddToCallLog(true)
214                     .setShouldShowNotification(true)
215                     .setContactExists(mContactExists)
216                     .build();
217         }
218         Log.addEvent(mCall, LogUtils.Events.BLOCK_CHECK_FINISHED,
219                 blockStatusToString(blockStatus) + " " + result);
220         resultFuture.complete(result);
221         mHandlerThread.quitSafely();
222         return blockStatus;
223     }
224 
getBlockReason(int blockStatus)225     private int getBlockReason(int blockStatus) {
226         switch (blockStatus) {
227             case STATUS_BLOCKED_IN_LIST:
228                 return CallLog.Calls.BLOCK_REASON_BLOCKED_NUMBER;
229 
230             case STATUS_BLOCKED_UNKNOWN_NUMBER:
231             case STATUS_BLOCKED_UNAVAILABLE:
232                 return CallLog.Calls.BLOCK_REASON_UNKNOWN_NUMBER;
233 
234             case STATUS_BLOCKED_RESTRICTED:
235                 return CallLog.Calls.BLOCK_REASON_RESTRICTED_NUMBER;
236 
237             case STATUS_BLOCKED_PAYPHONE:
238                 return CallLog.Calls.BLOCK_REASON_PAY_PHONE;
239 
240             case STATUS_BLOCKED_NOT_IN_CONTACTS:
241                 return CallLog.Calls.BLOCK_REASON_NOT_IN_CONTACTS;
242 
243             default:
244                 Log.w(this,
245                         "There's no call log block reason can be converted");
246                 return CallLog.Calls.BLOCK_REASON_BLOCKED_NUMBER;
247         }
248     }
249 
250     /**
251      * Converts a block status constant to a string equivalent for logging.
252      */
blockStatusToString(int blockStatus)253     private String blockStatusToString(int blockStatus) {
254         switch (blockStatus) {
255             case STATUS_NOT_BLOCKED:
256                 return "not blocked";
257             case STATUS_BLOCKED_IN_LIST:
258                 return "blocked - in list";
259             case STATUS_BLOCKED_RESTRICTED:
260                 return "blocked - restricted";
261             case STATUS_BLOCKED_UNKNOWN_NUMBER:
262                 return "blocked - unknown";
263             case STATUS_BLOCKED_PAYPHONE:
264                 return "blocked - payphone";
265             case STATUS_BLOCKED_NOT_IN_CONTACTS:
266                 return "blocked - not in contacts";
267             case STATUS_BLOCKED_UNAVAILABLE:
268                 return "blocked - unavailable";
269         }
270         return "unknown";
271     }
272 }
273