blob: 6bb49057458bb7c96816667222bb7262bf1252b1 [file] [log] [blame]
Sailesh Nepal1bef3392016-01-24 18:21:53 -08001/*
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
17package android.telecom;
18
Hall Liuff4a1252020-01-01 16:27:14 -080019import android.Manifest;
Tyler Gunn7e45b722018-12-04 12:56:45 -080020import android.annotation.NonNull;
Hall Liuff4a1252020-01-01 16:27:14 -080021import android.annotation.RequiresPermission;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080022import android.annotation.SdkConstant;
Hall Liu6dfa2492019-10-01 17:20:39 -070023import android.annotation.SystemApi;
24import android.annotation.TestApi;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080025import android.app.Service;
tonyzhu9e1d4f82018-10-22 15:11:31 +080026import android.content.ComponentName;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080027import android.content.Intent;
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080028import android.net.Uri;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080029import android.os.Handler;
30import android.os.IBinder;
31import android.os.Looper;
32import android.os.Message;
33import android.os.RemoteException;
34
35import com.android.internal.os.SomeArgs;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080036import com.android.internal.telecom.ICallScreeningAdapter;
Tyler Gunne0caec72018-11-30 14:21:18 -080037import com.android.internal.telecom.ICallScreeningService;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080038
39/**
40 * This service can be implemented by the default dialer (see
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080041 * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
Tyler Gunna842e7622019-03-29 11:32:08 -070042 * 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.
Sailesh Nepal1bef3392016-01-24 18:21:53 -080044 * <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>
Tyler Gunn7e45b722018-12-04 12:56:45 -080056 * <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>
Tyler Gunna842e7622019-03-29 11:32:08 -070061 * <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>
Tyler Gunn7e45b722018-12-04 12:56:45 -080064 * </ol>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080065 * <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);
Grace Jia2d21e952019-09-20 14:57:00 -070078 * Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING);
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080079 * 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>
Sailesh Nepal1bef3392016-01-24 18:21:53 -080093 */
94public 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;
Grace Jia90b38042019-11-06 14:12:33 -0800111 Call.Details callDetails = Call.Details
112 .createFromParcelableCall((ParcelableCall) args.arg2);
113 onScreenCall(callDetails);
114 if (callDetails.getCallDirection() == Call.Details.DIRECTION_OUTGOING) {
115 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
116 }
117 } catch (RemoteException e) {
118 Log.w(this, "Exception when screening call: " + e);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800119 } finally {
120 args.recycle();
121 }
122 break;
123 }
124 }
125 };
126
127 private final class CallScreeningBinder extends ICallScreeningService.Stub {
128 @Override
129 public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) {
130 Log.v(this, "screenCall");
131 SomeArgs args = SomeArgs.obtain();
132 args.arg1 = adapter;
133 args.arg2 = call;
134 mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget();
135 }
136 }
137
138 private ICallScreeningAdapter mCallScreeningAdapter;
139
140 /*
141 * Information about how to respond to an incoming call.
142 */
Sailesh Nepalf4460712016-01-27 16:45:51 -0800143 public static class CallResponse {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800144 private final boolean mShouldDisallowCall;
145 private final boolean mShouldRejectCall;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800146 private final boolean mShouldSilenceCall;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800147 private final boolean mShouldSkipCallLog;
148 private final boolean mShouldSkipNotification;
Hall Liu69554cf2019-11-11 17:44:09 -0800149 private final boolean mShouldScreenCallViaAudioProcessing;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800150
151 private CallResponse(
152 boolean shouldDisallowCall,
153 boolean shouldRejectCall,
Usman Abdullah47b392d2019-03-06 15:54:56 -0800154 boolean shouldSilenceCall,
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800155 boolean shouldSkipCallLog,
Hall Liu6dfa2492019-10-01 17:20:39 -0700156 boolean shouldSkipNotification,
Hall Liu69554cf2019-11-11 17:44:09 -0800157 boolean shouldScreenCallViaAudioProcessing) {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800158 if (!shouldDisallowCall
159 && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
160 throw new IllegalStateException("Invalid response state for allowed call.");
161 }
162
Hall Liu69554cf2019-11-11 17:44:09 -0800163 if (shouldDisallowCall && shouldScreenCallViaAudioProcessing) {
Hall Liu6dfa2492019-10-01 17:20:39 -0700164 throw new IllegalStateException("Invalid response state for allowed call.");
165 }
166
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800167 mShouldDisallowCall = shouldDisallowCall;
168 mShouldRejectCall = shouldRejectCall;
169 mShouldSkipCallLog = shouldSkipCallLog;
170 mShouldSkipNotification = shouldSkipNotification;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800171 mShouldSilenceCall = shouldSilenceCall;
Hall Liu69554cf2019-11-11 17:44:09 -0800172 mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800173 }
174
175 /*
176 * @return Whether the incoming call should be blocked.
177 */
178 public boolean getDisallowCall() {
179 return mShouldDisallowCall;
180 }
181
182 /*
183 * @return Whether the incoming call should be disconnected as if the user had manually
184 * rejected it.
185 */
186 public boolean getRejectCall() {
187 return mShouldRejectCall;
188 }
189
190 /*
Usman Abdullah47b392d2019-03-06 15:54:56 -0800191 * @return Whether the ringtone should be silenced for the incoming call.
192 */
193 public boolean getSilenceCall() {
194 return mShouldSilenceCall;
195 }
196
197 /*
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800198 * @return Whether the incoming call should not be displayed in the call log.
199 */
200 public boolean getSkipCallLog() {
201 return mShouldSkipCallLog;
202 }
203
204 /*
205 * @return Whether a missed call notification should not be shown for the incoming call.
206 */
207 public boolean getSkipNotification() {
208 return mShouldSkipNotification;
209 }
210
Hall Liu6dfa2492019-10-01 17:20:39 -0700211 /**
212 * @return Whether we should enter the {@link Call#STATE_AUDIO_PROCESSING} state to allow
213 * for further screening of the call.
214 * @hide
215 */
Hall Liu69554cf2019-11-11 17:44:09 -0800216 public boolean getShouldScreenCallViaAudioProcessing() {
217 return mShouldScreenCallViaAudioProcessing;
Hall Liu6dfa2492019-10-01 17:20:39 -0700218 }
219
Sailesh Nepalf4460712016-01-27 16:45:51 -0800220 public static class Builder {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800221 private boolean mShouldDisallowCall;
222 private boolean mShouldRejectCall;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800223 private boolean mShouldSilenceCall;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800224 private boolean mShouldSkipCallLog;
225 private boolean mShouldSkipNotification;
Hall Liu69554cf2019-11-11 17:44:09 -0800226 private boolean mShouldScreenCallViaAudioProcessing;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800227
Tyler Gunne0caec72018-11-30 14:21:18 -0800228 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800229 * Sets whether the incoming call should be blocked.
230 */
231 public Builder setDisallowCall(boolean shouldDisallowCall) {
232 mShouldDisallowCall = shouldDisallowCall;
233 return this;
234 }
235
Tyler Gunne0caec72018-11-30 14:21:18 -0800236 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800237 * Sets whether the incoming call should be disconnected as if the user had manually
238 * rejected it. This property should only be set to true if the call is disallowed.
239 */
240 public Builder setRejectCall(boolean shouldRejectCall) {
241 mShouldRejectCall = shouldRejectCall;
242 return this;
243 }
244
Tyler Gunne0caec72018-11-30 14:21:18 -0800245 /**
Usman Abdullah47b392d2019-03-06 15:54:56 -0800246 * Sets whether ringing should be silenced for the incoming call. When set
247 * to {@code true}, the Telecom framework will not play a ringtone for the call.
248 * The call will, however, still be sent to the default dialer app if it is not blocked.
249 * A {@link CallScreeningService} can use this to ensure a potential nuisance call is
250 * still surfaced to the user, but in a less intrusive manner.
251 *
252 * Setting this to true only makes sense when the call has not been disallowed
253 * using {@link #setDisallowCall(boolean)}.
254 */
255 public @NonNull Builder setSilenceCall(boolean shouldSilenceCall) {
256 mShouldSilenceCall = shouldSilenceCall;
257 return this;
258 }
259
260 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800261 * Sets whether the incoming call should not be displayed in the call log. This property
262 * should only be set to true if the call is disallowed.
Tyler Gunne0caec72018-11-30 14:21:18 -0800263 * <p>
264 * Note: Calls will still be logged with type
265 * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
266 * is set.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800267 */
268 public Builder setSkipCallLog(boolean shouldSkipCallLog) {
269 mShouldSkipCallLog = shouldSkipCallLog;
270 return this;
271 }
272
Tyler Gunne0caec72018-11-30 14:21:18 -0800273 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800274 * Sets whether a missed call notification should not be shown for the incoming call.
275 * This property should only be set to true if the call is disallowed.
276 */
277 public Builder setSkipNotification(boolean shouldSkipNotification) {
278 mShouldSkipNotification = shouldSkipNotification;
279 return this;
280 }
281
Hall Liu6dfa2492019-10-01 17:20:39 -0700282 /**
283 * Sets whether to request background audio processing so that the in-call service can
284 * screen the call further. If set to {@code true}, {@link #setDisallowCall} should be
285 * called with {@code false}, and all other parameters in this builder will be ignored.
286 *
287 * This request will only be honored if the {@link CallScreeningService} shares the same
288 * uid as the default dialer app. Otherwise, the call will go through as usual.
289 *
Hall Liu69554cf2019-11-11 17:44:09 -0800290 * @param shouldScreenCallViaAudioProcessing Whether to request further call screening.
Hall Liu6dfa2492019-10-01 17:20:39 -0700291 * @hide
292 */
293 @SystemApi
294 @TestApi
Hall Liuff4a1252020-01-01 16:27:14 -0800295 @RequiresPermission(Manifest.permission.CAPTURE_AUDIO_OUTPUT)
Hall Liu69554cf2019-11-11 17:44:09 -0800296 public @NonNull Builder setShouldScreenCallViaAudioProcessing(
297 boolean shouldScreenCallViaAudioProcessing) {
298 mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
Hall Liu6dfa2492019-10-01 17:20:39 -0700299 return this;
300 }
301
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800302 public CallResponse build() {
303 return new CallResponse(
304 mShouldDisallowCall,
305 mShouldRejectCall,
Usman Abdullah47b392d2019-03-06 15:54:56 -0800306 mShouldSilenceCall,
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800307 mShouldSkipCallLog,
Hall Liu6dfa2492019-10-01 17:20:39 -0700308 mShouldSkipNotification,
Hall Liu69554cf2019-11-11 17:44:09 -0800309 mShouldScreenCallViaAudioProcessing);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800310 }
311 }
312 }
313
314 public CallScreeningService() {
315 }
316
317 @Override
318 public IBinder onBind(Intent intent) {
319 Log.v(this, "onBind");
320 return new CallScreeningBinder();
321 }
322
323 @Override
324 public boolean onUnbind(Intent intent) {
325 Log.v(this, "onUnbind");
326 return false;
327 }
328
329 /**
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800330 * Called when a new incoming or outgoing call is added which is not in the user's contact list.
331 * <p>
332 * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by
333 * calling
334 * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}.
335 * Your app can tell if a call is an incoming call by checking to see if
336 * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}.
337 * <p>
Tyler Gunne0caec72018-11-30 14:21:18 -0800338 * Note: The {@link Call.Details} instance provided to a call screening service will only have
339 * the following properties set. The rest of the {@link Call.Details} properties will be set to
340 * their default value or {@code null}.
341 * <ul>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800342 * <li>{@link Call.Details#getCallDirection()}</li>
Tyler Gunne0caec72018-11-30 14:21:18 -0800343 * <li>{@link Call.Details#getConnectTimeMillis()}</li>
344 * <li>{@link Call.Details#getCreationTimeMillis()}</li>
345 * <li>{@link Call.Details#getHandle()}</li>
346 * <li>{@link Call.Details#getHandlePresentation()}</li>
347 * </ul>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800348 * <p>
349 * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme}
350 * is {@link PhoneAccount#SCHEME_TEL} are passed for call
351 * screening. Further, only calls which are not in the user's contacts are passed for
352 * screening. For outgoing calls, no post-dial digits are passed.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800353 *
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800354 * @param callDetails Information about a new call, see {@link Call.Details}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800355 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800356 public abstract void onScreenCall(@NonNull Call.Details callDetails);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800357
358 /**
Usman Abdullah47b392d2019-03-06 15:54:56 -0800359 * Responds to the given incoming call, either allowing it, silencing it or disallowing it.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800360 * <p>
361 * The {@link CallScreeningService} calls this method to inform the system whether the call
Usman Abdullah47b392d2019-03-06 15:54:56 -0800362 * should be silently blocked or not. In the event that it should not be blocked, it may
363 * also be requested to ring silently.
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800364 * <p>
365 * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is
366 * {@link Call.Details#DIRECTION_INCOMING}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800367 *
368 * @param callDetails The call to allow.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800369 * <p>
370 * Must be the same {@link Call.Details call} which was provided to the
371 * {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800372 * @param response The {@link CallScreeningService.CallResponse} which contains information
373 * about how to respond to a call.
374 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800375 public final void respondToCall(@NonNull Call.Details callDetails,
376 @NonNull CallResponse response) {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800377 try {
378 if (response.getDisallowCall()) {
379 mCallScreeningAdapter.disallowCall(
380 callDetails.getTelecomCallId(),
381 response.getRejectCall(),
382 !response.getSkipCallLog(),
tonyzhu9e1d4f82018-10-22 15:11:31 +0800383 !response.getSkipNotification(),
384 new ComponentName(getPackageName(), getClass().getName()));
Usman Abdullah47b392d2019-03-06 15:54:56 -0800385 } else if (response.getSilenceCall()) {
386 mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId());
Hall Liu69554cf2019-11-11 17:44:09 -0800387 } else if (response.getShouldScreenCallViaAudioProcessing()) {
Hall Liu31de23d2019-10-11 15:38:29 -0700388 mCallScreeningAdapter.screenCallFurther(callDetails.getTelecomCallId());
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800389 } else {
390 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
391 }
392 } catch (RemoteException e) {
393 }
394 }
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800395}