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