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 android.telecom;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.SdkConstant;
22 import android.app.Service;
23 import android.content.ComponentName;
24 import android.content.Intent;
25 import android.net.Uri;
26 import android.os.Handler;
27 import android.os.IBinder;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.os.RemoteException;
31 
32 import com.android.internal.os.SomeArgs;
33 import com.android.internal.telecom.ICallScreeningAdapter;
34 import com.android.internal.telecom.ICallScreeningService;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 
39 /**
40  * This service can be implemented by the default dialer (see
41  * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
42  * incoming calls before they are shown to a user. A {@link CallScreeningService} can also see
43  * outgoing calls for the purpose of providing caller ID services for those calls.
44  * <p>
45  * Below is an example manifest registration for a {@code CallScreeningService}.
46  * <pre>
47  * {@code
48  * <service android:name="your.package.YourCallScreeningServiceImplementation"
49  *          android:permission="android.permission.BIND_SCREENING_SERVICE">
50  *      <intent-filter>
51  *          <action android:name="android.telecom.CallScreeningService"/>
52  *      </intent-filter>
53  * </service>
54  * }
55  * </pre>
56  * <p>
57  * A CallScreeningService performs two functions:
58  * <ol>
59  *     <li>Call blocking/screening - the service can choose which calls will ring on the user's
60  *     device, and which will be silently sent to voicemail.</li>
61  *     <li>Call identification - services which provide call identification functionality can
62  *     display a user-interface of their choosing which contains identifying information for a call.
63  *     </li>
64  * </ol>
65  * <p>
66  * <h2>Becoming the {@link CallScreeningService}</h2>
67  * Telecom will bind to a single app chosen by the user which implements the
68  * {@link CallScreeningService} API when there are new incoming and outgoing calls.
69  * <p>
70  * The code snippet below illustrates how your app can request that it fills the call screening
71  * role.
72  * <pre>
73  * {@code
74  * private static final int REQUEST_ID = 1;
75  *
76  * public void requestRole() {
77  *     RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
78  *     Intent intent = roleManager.createRequestRoleIntent("android.app.role.CALL_SCREENING");
79  *     startActivityForResult(intent, REQUEST_ID);
80  * }
81  *
82  * &#64;Override
83  * public void onActivityResult(int requestCode, int resultCode, Intent data) {
84  *     if (requestCode == REQUEST_ID) {
85  *         if (resultCode == android.app.Activity.RESULT_OK) {
86  *             // Your app is now the call screening app
87  *         } else {
88  *             // Your app is not the call screening app
89  *         }
90  *     }
91  * }
92  * </pre>
93  */
94 public abstract class CallScreeningService extends Service {
95     /**
96      * The {@link Intent} that must be declared as handled by the service.
97      */
98     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
99     public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
100 
101     private static final int MSG_SCREEN_CALL = 1;
102 
103     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
104         @Override
105         public void handleMessage(Message msg) {
106             switch (msg.what) {
107                 case MSG_SCREEN_CALL:
108                     SomeArgs args = (SomeArgs) msg.obj;
109                     try {
110                         mCallScreeningAdapter = (ICallScreeningAdapter) args.arg1;
111                         onScreenCall(
112                                 Call.Details.createFromParcelableCall((ParcelableCall) args.arg2));
113                     } finally {
114                         args.recycle();
115                     }
116                     break;
117             }
118         }
119     };
120 
121     private final class CallScreeningBinder extends ICallScreeningService.Stub {
122         @Override
screenCall(ICallScreeningAdapter adapter, ParcelableCall call)123         public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) {
124             Log.v(this, "screenCall");
125             SomeArgs args = SomeArgs.obtain();
126             args.arg1 = adapter;
127             args.arg2 = call;
128             mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget();
129         }
130     }
131 
132     private ICallScreeningAdapter mCallScreeningAdapter;
133 
134     /*
135      * Information about how to respond to an incoming call.
136      */
137     public static class CallResponse {
138         private final boolean mShouldDisallowCall;
139         private final boolean mShouldRejectCall;
140         private final boolean mShouldSilenceCall;
141         private final boolean mShouldSkipCallLog;
142         private final boolean mShouldSkipNotification;
143 
CallResponse( boolean shouldDisallowCall, boolean shouldRejectCall, boolean shouldSilenceCall, boolean shouldSkipCallLog, boolean shouldSkipNotification)144         private CallResponse(
145                 boolean shouldDisallowCall,
146                 boolean shouldRejectCall,
147                 boolean shouldSilenceCall,
148                 boolean shouldSkipCallLog,
149                 boolean shouldSkipNotification) {
150             if (!shouldDisallowCall
151                     && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
152                 throw new IllegalStateException("Invalid response state for allowed call.");
153             }
154 
155             mShouldDisallowCall = shouldDisallowCall;
156             mShouldRejectCall = shouldRejectCall;
157             mShouldSkipCallLog = shouldSkipCallLog;
158             mShouldSkipNotification = shouldSkipNotification;
159             mShouldSilenceCall = shouldSilenceCall;
160         }
161 
162         /*
163          * @return Whether the incoming call should be blocked.
164          */
getDisallowCall()165         public boolean getDisallowCall() {
166             return mShouldDisallowCall;
167         }
168 
169         /*
170          * @return Whether the incoming call should be disconnected as if the user had manually
171          * rejected it.
172          */
getRejectCall()173         public boolean getRejectCall() {
174             return mShouldRejectCall;
175         }
176 
177         /*
178          * @return Whether the ringtone should be silenced for the incoming call.
179          */
getSilenceCall()180         public boolean getSilenceCall() {
181             return mShouldSilenceCall;
182         }
183 
184         /*
185          * @return Whether the incoming call should not be displayed in the call log.
186          */
getSkipCallLog()187         public boolean getSkipCallLog() {
188             return mShouldSkipCallLog;
189         }
190 
191         /*
192          * @return Whether a missed call notification should not be shown for the incoming call.
193          */
getSkipNotification()194         public boolean getSkipNotification() {
195             return mShouldSkipNotification;
196         }
197 
198         public static class Builder {
199             private boolean mShouldDisallowCall;
200             private boolean mShouldRejectCall;
201             private boolean mShouldSilenceCall;
202             private boolean mShouldSkipCallLog;
203             private boolean mShouldSkipNotification;
204 
205             /**
206              * Sets whether the incoming call should be blocked.
207              */
setDisallowCall(boolean shouldDisallowCall)208             public Builder setDisallowCall(boolean shouldDisallowCall) {
209                 mShouldDisallowCall = shouldDisallowCall;
210                 return this;
211             }
212 
213             /**
214              * Sets whether the incoming call should be disconnected as if the user had manually
215              * rejected it. This property should only be set to true if the call is disallowed.
216              */
setRejectCall(boolean shouldRejectCall)217             public Builder setRejectCall(boolean shouldRejectCall) {
218                 mShouldRejectCall = shouldRejectCall;
219                 return this;
220             }
221 
222             /**
223              * Sets whether ringing should be silenced for the incoming call.  When set
224              * to {@code true}, the Telecom framework will not play a ringtone for the call.
225              * The call will, however, still be sent to the default dialer app if it is not blocked.
226              * A {@link CallScreeningService} can use this to ensure a potential nuisance call is
227              * still surfaced to the user, but in a less intrusive manner.
228              *
229              * Setting this to true only makes sense when the call has not been disallowed
230              * using {@link #setDisallowCall(boolean)}.
231              */
setSilenceCall(boolean shouldSilenceCall)232             public @NonNull Builder setSilenceCall(boolean shouldSilenceCall) {
233                 mShouldSilenceCall = shouldSilenceCall;
234                 return this;
235             }
236 
237             /**
238              * Sets whether the incoming call should not be displayed in the call log. This property
239              * should only be set to true if the call is disallowed.
240              * <p>
241              * Note: Calls will still be logged with type
242              * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
243              * is set.
244              */
setSkipCallLog(boolean shouldSkipCallLog)245             public Builder setSkipCallLog(boolean shouldSkipCallLog) {
246                 mShouldSkipCallLog = shouldSkipCallLog;
247                 return this;
248             }
249 
250             /**
251              * Sets whether a missed call notification should not be shown for the incoming call.
252              * This property should only be set to true if the call is disallowed.
253              */
setSkipNotification(boolean shouldSkipNotification)254             public Builder setSkipNotification(boolean shouldSkipNotification) {
255                 mShouldSkipNotification = shouldSkipNotification;
256                 return this;
257             }
258 
build()259             public CallResponse build() {
260                 return new CallResponse(
261                         mShouldDisallowCall,
262                         mShouldRejectCall,
263                         mShouldSilenceCall,
264                         mShouldSkipCallLog,
265                         mShouldSkipNotification);
266             }
267        }
268     }
269 
CallScreeningService()270     public CallScreeningService() {
271     }
272 
273     @Override
onBind(Intent intent)274     public IBinder onBind(Intent intent) {
275         Log.v(this, "onBind");
276         return new CallScreeningBinder();
277     }
278 
279     @Override
onUnbind(Intent intent)280     public boolean onUnbind(Intent intent) {
281         Log.v(this, "onUnbind");
282         return false;
283     }
284 
285     /**
286      * Called when a new incoming or outgoing call is added which is not in the user's contact list.
287      * <p>
288      * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by
289      * calling
290      * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}.
291      * Your app can tell if a call is an incoming call by checking to see if
292      * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}.
293      * <p>
294      * Note: The {@link Call.Details} instance provided to a call screening service will only have
295      * the following properties set.  The rest of the {@link Call.Details} properties will be set to
296      * their default value or {@code null}.
297      * <ul>
298      *     <li>{@link Call.Details#getCallDirection()}</li>
299      *     <li>{@link Call.Details#getConnectTimeMillis()}</li>
300      *     <li>{@link Call.Details#getCreationTimeMillis()}</li>
301      *     <li>{@link Call.Details#getHandle()}</li>
302      *     <li>{@link Call.Details#getHandlePresentation()}</li>
303      * </ul>
304      * <p>
305      * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme}
306      * is {@link PhoneAccount#SCHEME_TEL} are passed for call
307      * screening.  Further, only calls which are not in the user's contacts are passed for
308      * screening.  For outgoing calls, no post-dial digits are passed.
309      *
310      * @param callDetails Information about a new call, see {@link Call.Details}.
311      */
onScreenCall(@onNull Call.Details callDetails)312     public abstract void onScreenCall(@NonNull Call.Details callDetails);
313 
314     /**
315      * Responds to the given incoming call, either allowing it, silencing it or disallowing it.
316      * <p>
317      * The {@link CallScreeningService} calls this method to inform the system whether the call
318      * should be silently blocked or not. In the event that it should not be blocked, it may
319      * also be requested to ring silently.
320      * <p>
321      * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is
322      * {@link Call.Details#DIRECTION_INCOMING}.
323      *
324      * @param callDetails The call to allow.
325      *                    <p>
326      *                    Must be the same {@link Call.Details call} which was provided to the
327      *                    {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
328      * @param response The {@link CallScreeningService.CallResponse} which contains information
329      * about how to respond to a call.
330      */
respondToCall(@onNull Call.Details callDetails, @NonNull CallResponse response)331     public final void respondToCall(@NonNull Call.Details callDetails,
332             @NonNull CallResponse response) {
333         try {
334             if (response.getDisallowCall()) {
335                 mCallScreeningAdapter.disallowCall(
336                         callDetails.getTelecomCallId(),
337                         response.getRejectCall(),
338                         !response.getSkipCallLog(),
339                         !response.getSkipNotification(),
340                         new ComponentName(getPackageName(), getClass().getName()));
341             } else if (response.getSilenceCall()) {
342                 mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId());
343             } else {
344                 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
345             }
346         } catch (RemoteException e) {
347         }
348     }
349 }
350