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