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.provider.CallLog; 20 import android.provider.CallLog.Calls; 21 import android.telecom.CallScreeningService; 22 import android.text.TextUtils; 23 24 import java.util.Objects; 25 26 public class CallFilteringResult { 27 public static class Builder { 28 private boolean mShouldAllowCall; 29 private boolean mShouldReject; 30 private boolean mShouldAddToCallLog; 31 private boolean mShouldShowNotification; 32 private boolean mDndSuppressed = false; 33 private boolean mShouldSilence = false; 34 private boolean mShouldScreenViaAudio = false; 35 private boolean mContactExists = false; 36 private int mCallBlockReason = Calls.BLOCK_REASON_NOT_BLOCKED; 37 private CharSequence mCallScreeningAppName = null; 38 private String mCallScreeningComponentName = null; 39 private CallScreeningService.ParcelableCallResponse mCallScreeningResponse = null; 40 private boolean mIsResponseFromSystemDialer = false; 41 setShouldAllowCall(boolean shouldAllowCall)42 public Builder setShouldAllowCall(boolean shouldAllowCall) { 43 mShouldAllowCall = shouldAllowCall; 44 return this; 45 } 46 setShouldReject(boolean shouldReject)47 public Builder setShouldReject(boolean shouldReject) { 48 mShouldReject = shouldReject; 49 return this; 50 } 51 setShouldAddToCallLog(boolean shouldAddToCallLog)52 public Builder setShouldAddToCallLog(boolean shouldAddToCallLog) { 53 mShouldAddToCallLog = shouldAddToCallLog; 54 return this; 55 } 56 setShouldShowNotification(boolean shouldShowNotification)57 public Builder setShouldShowNotification(boolean shouldShowNotification) { 58 mShouldShowNotification = shouldShowNotification; 59 return this; 60 } 61 setDndSuppressed(boolean shouldPerformCheck)62 public Builder setDndSuppressed(boolean shouldPerformCheck) { 63 mDndSuppressed = shouldPerformCheck; 64 return this; 65 } 66 setShouldSilence(boolean shouldSilence)67 public Builder setShouldSilence(boolean shouldSilence) { 68 mShouldSilence = shouldSilence; 69 return this; 70 } 71 setCallBlockReason(int callBlockReason)72 public Builder setCallBlockReason(int callBlockReason) { 73 mCallBlockReason = callBlockReason; 74 return this; 75 } 76 setShouldScreenViaAudio(boolean shouldScreenViaAudio)77 public Builder setShouldScreenViaAudio(boolean shouldScreenViaAudio) { 78 mShouldScreenViaAudio = shouldScreenViaAudio; 79 return this; 80 } 81 setCallScreeningAppName(CharSequence callScreeningAppName)82 public Builder setCallScreeningAppName(CharSequence callScreeningAppName) { 83 mCallScreeningAppName = callScreeningAppName; 84 return this; 85 } 86 setCallScreeningComponentName(String callScreeningComponentName)87 public Builder setCallScreeningComponentName(String callScreeningComponentName) { 88 mCallScreeningComponentName = callScreeningComponentName; 89 return this; 90 } 91 setCallScreeningResponse( CallScreeningService.ParcelableCallResponse response, boolean isFromSystemDialer)92 public Builder setCallScreeningResponse( 93 CallScreeningService.ParcelableCallResponse response, boolean isFromSystemDialer) { 94 mCallScreeningResponse = response; 95 mIsResponseFromSystemDialer = isFromSystemDialer; 96 return this; 97 } 98 setContactExists(boolean contactExists)99 public Builder setContactExists(boolean contactExists) { 100 mContactExists = contactExists; 101 return this; 102 } 103 from(CallFilteringResult result)104 public static Builder from(CallFilteringResult result) { 105 return new Builder() 106 .setShouldAllowCall(result.shouldAllowCall) 107 .setShouldReject(result.shouldReject) 108 .setShouldAddToCallLog(result.shouldAddToCallLog) 109 .setShouldShowNotification(result.shouldShowNotification) 110 .setDndSuppressed(result.shouldSuppressCallDueToDndStatus) 111 .setShouldSilence(result.shouldSilence) 112 .setCallBlockReason(result.mCallBlockReason) 113 .setShouldScreenViaAudio(result.shouldScreenViaAudio) 114 .setCallScreeningAppName(result.mCallScreeningAppName) 115 .setCallScreeningComponentName(result.mCallScreeningComponentName) 116 .setCallScreeningResponse(result.mCallScreeningResponse, 117 result.mIsResponseFromSystemDialer) 118 .setContactExists(result.contactExists); 119 } 120 build()121 public CallFilteringResult build() { 122 return new CallFilteringResult(mShouldAllowCall, mShouldReject, mShouldSilence, 123 mShouldAddToCallLog, mShouldShowNotification, 124 mDndSuppressed, mCallBlockReason, mCallScreeningAppName, 125 mCallScreeningComponentName, mCallScreeningResponse, 126 mIsResponseFromSystemDialer, mShouldScreenViaAudio, mContactExists); 127 } 128 } 129 130 public boolean shouldAllowCall; 131 public boolean shouldReject; 132 public boolean shouldSilence; 133 public boolean shouldAddToCallLog; 134 public boolean shouldScreenViaAudio = false; 135 public boolean shouldShowNotification; 136 public boolean shouldSuppressCallDueToDndStatus = false; 137 public int mCallBlockReason; 138 public CharSequence mCallScreeningAppName; 139 public String mCallScreeningComponentName; 140 public CallScreeningService.ParcelableCallResponse mCallScreeningResponse; 141 public boolean mIsResponseFromSystemDialer; 142 public boolean contactExists; 143 CallFilteringResult(boolean shouldAllowCall, boolean shouldReject, boolean shouldSilence, boolean shouldAddToCallLog, boolean shouldShowNotification, boolean shouldSuppress, int callBlockReason, CharSequence callScreeningAppName, String callScreeningComponentName, CallScreeningService.ParcelableCallResponse callScreeningResponse, boolean isResponseFromSystemDialer, boolean shouldScreenViaAudio, boolean contactExists)144 private CallFilteringResult(boolean shouldAllowCall, boolean shouldReject, boolean 145 shouldSilence, boolean shouldAddToCallLog, boolean shouldShowNotification, boolean 146 shouldSuppress, int callBlockReason, CharSequence callScreeningAppName, 147 String callScreeningComponentName, 148 CallScreeningService.ParcelableCallResponse callScreeningResponse, 149 boolean isResponseFromSystemDialer, 150 boolean shouldScreenViaAudio, boolean contactExists) { 151 this.shouldAllowCall = shouldAllowCall; 152 this.shouldReject = shouldReject; 153 this.shouldSilence = shouldSilence; 154 this.shouldAddToCallLog = shouldAddToCallLog; 155 this.shouldShowNotification = shouldShowNotification; 156 this.shouldSuppressCallDueToDndStatus = shouldSuppress; 157 this.shouldScreenViaAudio = shouldScreenViaAudio; 158 this.mCallBlockReason = callBlockReason; 159 this.mCallScreeningAppName = callScreeningAppName; 160 this.mCallScreeningComponentName = callScreeningComponentName; 161 this.mCallScreeningResponse = callScreeningResponse; 162 this.mIsResponseFromSystemDialer = isResponseFromSystemDialer; 163 this.contactExists = contactExists; 164 } 165 166 /** 167 * Combine this CallFilteringResult with another, returning a CallFilteringResult with the more 168 * restrictive properties of the two. Where there are multiple call filtering components which 169 * block a call, the first filter from {@link BlockCheckerFilter}, 170 * {@link DirectToVoicemailFilter}, {@link CallScreeningServiceFilter} which blocked a call 171 * shall be used to populate the call block reason, component name, etc. 172 */ combine(CallFilteringResult other)173 public CallFilteringResult combine(CallFilteringResult other) { 174 if (other == null) { 175 return this; 176 } 177 178 if (isBlockedByProvider(mCallBlockReason)) { 179 return getCombinedCallFilteringResult(other, mCallBlockReason, 180 null /*callScreeningAppName*/, null /*callScreeningComponentName*/); 181 } else if (isBlockedByProvider(other.mCallBlockReason)) { 182 return getCombinedCallFilteringResult(other, other.mCallBlockReason, 183 null /*callScreeningAppName*/, null /*callScreeningComponentName*/); 184 } 185 186 if (mCallBlockReason == Calls.BLOCK_REASON_DIRECT_TO_VOICEMAIL 187 || other.mCallBlockReason == Calls.BLOCK_REASON_DIRECT_TO_VOICEMAIL) { 188 return getCombinedCallFilteringResult(other, Calls.BLOCK_REASON_DIRECT_TO_VOICEMAIL, 189 null /*callScreeningAppName*/, null /*callScreeningComponentName*/); 190 } 191 192 if (shouldReject && mCallBlockReason == CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE) { 193 return getCombinedCallFilteringResult(other, Calls.BLOCK_REASON_CALL_SCREENING_SERVICE, 194 mCallScreeningAppName, mCallScreeningComponentName); 195 } else if (other.shouldReject && other.mCallBlockReason == CallLog.Calls 196 .BLOCK_REASON_CALL_SCREENING_SERVICE) { 197 return getCombinedCallFilteringResult(other, Calls.BLOCK_REASON_CALL_SCREENING_SERVICE, 198 other.mCallScreeningAppName, other.mCallScreeningComponentName); 199 } 200 201 if (shouldScreenViaAudio) { 202 return getCombinedCallFilteringResult(other, Calls.BLOCK_REASON_NOT_BLOCKED, 203 mCallScreeningAppName, mCallScreeningComponentName); 204 } else if (other.shouldScreenViaAudio) { 205 return getCombinedCallFilteringResult(other, Calls.BLOCK_REASON_NOT_BLOCKED, 206 other.mCallScreeningAppName, other.mCallScreeningComponentName); 207 } 208 209 Builder b = new Builder() 210 .setShouldAllowCall(shouldAllowCall && other.shouldAllowCall) 211 .setShouldReject(shouldReject || other.shouldReject) 212 .setShouldSilence(shouldSilence || other.shouldSilence) 213 .setShouldAddToCallLog(shouldAddToCallLog && other.shouldAddToCallLog) 214 .setShouldShowNotification(shouldShowNotification && other.shouldShowNotification) 215 .setShouldScreenViaAudio(shouldScreenViaAudio || other.shouldScreenViaAudio) 216 .setDndSuppressed(shouldSuppressCallDueToDndStatus 217 || other.shouldSuppressCallDueToDndStatus) 218 .setContactExists(contactExists || other.contactExists); 219 combineScreeningResponses(b, this, other); 220 return b.build(); 221 } 222 isBlockedByProvider(int blockReason)223 private boolean isBlockedByProvider(int blockReason) { 224 if (blockReason == Calls.BLOCK_REASON_BLOCKED_NUMBER 225 || blockReason == Calls.BLOCK_REASON_UNKNOWN_NUMBER 226 || blockReason == Calls.BLOCK_REASON_RESTRICTED_NUMBER 227 || blockReason == Calls.BLOCK_REASON_PAY_PHONE 228 || blockReason == Calls.BLOCK_REASON_NOT_IN_CONTACTS) { 229 return true; 230 } 231 232 return false; 233 } 234 getCombinedCallFilteringResult(CallFilteringResult other, int callBlockReason, CharSequence callScreeningAppName, String callScreeningComponentName)235 private CallFilteringResult getCombinedCallFilteringResult(CallFilteringResult other, 236 int callBlockReason, CharSequence callScreeningAppName, 237 String callScreeningComponentName) { 238 Builder b = new Builder() 239 .setShouldAllowCall(shouldAllowCall && other.shouldAllowCall) 240 .setShouldReject(shouldReject || other.shouldReject) 241 .setShouldSilence(shouldSilence || other.shouldSilence) 242 .setShouldAddToCallLog(shouldAddToCallLog && other.shouldAddToCallLog) 243 .setShouldShowNotification(shouldShowNotification && other.shouldShowNotification) 244 .setDndSuppressed(shouldSuppressCallDueToDndStatus 245 || other.shouldSuppressCallDueToDndStatus) 246 .setShouldScreenViaAudio(shouldScreenViaAudio || other.shouldScreenViaAudio) 247 .setCallBlockReason(callBlockReason) 248 .setCallScreeningAppName(callScreeningAppName) 249 .setCallScreeningComponentName(callScreeningComponentName) 250 .setContactExists(contactExists || other.contactExists); 251 combineScreeningResponses(b, this, other); 252 return b.build(); 253 } 254 combineScreeningResponses(Builder builder, CallFilteringResult r1, CallFilteringResult r2)255 private static void combineScreeningResponses(Builder builder, CallFilteringResult r1, 256 CallFilteringResult r2) { 257 if (r1.mIsResponseFromSystemDialer) { 258 builder.setCallScreeningResponse(r1.mCallScreeningResponse, true); 259 builder.setCallScreeningComponentName(r1.mCallScreeningComponentName); 260 builder.setCallScreeningAppName(r1.mCallScreeningAppName); 261 } else if (r2.mIsResponseFromSystemDialer) { 262 builder.setCallScreeningResponse(r2.mCallScreeningResponse, true); 263 builder.setCallScreeningComponentName(r2.mCallScreeningComponentName); 264 builder.setCallScreeningAppName(r2.mCallScreeningAppName); 265 } else { 266 if (r1.mCallScreeningResponse != null) { 267 builder.setCallScreeningResponse(r1.mCallScreeningResponse, false); 268 builder.setCallScreeningComponentName(r1.mCallScreeningComponentName); 269 builder.setCallScreeningAppName(r1.mCallScreeningAppName); 270 } else { 271 builder.setCallScreeningResponse(r2.mCallScreeningResponse, false); 272 builder.setCallScreeningComponentName(r2.mCallScreeningComponentName); 273 builder.setCallScreeningAppName(r2.mCallScreeningAppName); 274 } 275 } 276 } 277 278 @Override equals(Object o)279 public boolean equals(Object o) { 280 if (this == o) return true; 281 if (o == null || getClass() != o.getClass()) return false; 282 283 CallFilteringResult that = (CallFilteringResult) o; 284 285 if (shouldAllowCall != that.shouldAllowCall) return false; 286 if (shouldReject != that.shouldReject) return false; 287 if (shouldSilence != that.shouldSilence) return false; 288 if (shouldAddToCallLog != that.shouldAddToCallLog) return false; 289 if (shouldShowNotification != that.shouldShowNotification) return false; 290 if (shouldSuppressCallDueToDndStatus != that.shouldSuppressCallDueToDndStatus) return false; 291 if (mCallBlockReason != that.mCallBlockReason) return false; 292 if (contactExists != that.contactExists) return false; 293 294 if (!Objects.equals(mCallScreeningAppName, that.mCallScreeningAppName)) return false; 295 if (!Objects.equals(mCallScreeningComponentName, that.mCallScreeningComponentName)) { 296 return false; 297 } 298 return true; 299 } 300 301 @Override hashCode()302 public int hashCode() { 303 int result = (shouldAllowCall ? 1 : 0); 304 result = 31 * result + (shouldReject ? 1 : 0); 305 result = 31 * result + (shouldSilence ? 1 : 0); 306 result = 31 * result + (shouldAddToCallLog ? 1 : 0); 307 result = 31 * result + (shouldShowNotification ? 1 : 0); 308 return result; 309 } 310 311 @Override toString()312 public String toString() { 313 StringBuilder sb = new StringBuilder(); 314 sb.append("["); 315 if (shouldAllowCall) { 316 sb.append("Allow"); 317 } else if (shouldReject) { 318 sb.append("Reject"); 319 } else if (shouldSilence) { 320 sb.append("Silence"); 321 } else { 322 sb.append("Ignore"); 323 } 324 325 if (shouldScreenViaAudio) { 326 sb.append(", audio processing"); 327 } 328 329 if (shouldAddToCallLog) { 330 sb.append(", logged"); 331 } 332 333 if (shouldShowNotification) { 334 sb.append(", notified"); 335 } 336 337 if (shouldSuppressCallDueToDndStatus) { 338 sb.append(", DND suppressed"); 339 } 340 341 if (contactExists) { 342 sb.append(", contact exists"); 343 } 344 345 if (mCallBlockReason != 0) { 346 sb.append(", mCallBlockReason = "); 347 sb.append(mCallBlockReason); 348 } 349 350 if (!TextUtils.isEmpty(mCallScreeningAppName)) { 351 sb.append(", mCallScreeningAppName = "); 352 sb.append(mCallScreeningAppName); 353 } 354 355 if (!TextUtils.isEmpty(mCallScreeningComponentName)) { 356 sb.append(", mCallScreeningComponentName = "); 357 sb.append(mCallScreeningComponentName); 358 } 359 sb.append("]"); 360 361 return sb.toString(); 362 } 363 } 364