blob: 4d9311c282f7abfcd5b47e0f0fdd8856d797575f [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 Gunn460b7d42020-05-15 10:19:32 -070028import android.content.pm.ServiceInfo;
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080029import android.net.Uri;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080030import android.os.Handler;
31import android.os.IBinder;
32import android.os.Looper;
33import android.os.Message;
34import android.os.RemoteException;
35
36import com.android.internal.os.SomeArgs;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080037import com.android.internal.telecom.ICallScreeningAdapter;
Tyler Gunne0caec72018-11-30 14:21:18 -080038import com.android.internal.telecom.ICallScreeningService;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080039
40/**
41 * This service can be implemented by the default dialer (see
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080042 * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
Tyler Gunna842e7622019-03-29 11:32:08 -070043 * incoming calls before they are shown to a user. A {@link CallScreeningService} can also see
44 * outgoing calls for the purpose of providing caller ID services for those calls.
Sailesh Nepal1bef3392016-01-24 18:21:53 -080045 * <p>
46 * Below is an example manifest registration for a {@code CallScreeningService}.
47 * <pre>
48 * {@code
49 * <service android:name="your.package.YourCallScreeningServiceImplementation"
50 * android:permission="android.permission.BIND_SCREENING_SERVICE">
51 * <intent-filter>
52 * <action android:name="android.telecom.CallScreeningService"/>
53 * </intent-filter>
54 * </service>
55 * }
56 * </pre>
Tyler Gunn7e45b722018-12-04 12:56:45 -080057 * <p>
58 * A CallScreeningService performs two functions:
59 * <ol>
60 * <li>Call blocking/screening - the service can choose which calls will ring on the user's
61 * device, and which will be silently sent to voicemail.</li>
Tyler Gunna842e7622019-03-29 11:32:08 -070062 * <li>Call identification - services which provide call identification functionality can
63 * display a user-interface of their choosing which contains identifying information for a call.
64 * </li>
Tyler Gunn7e45b722018-12-04 12:56:45 -080065 * </ol>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080066 * <p>
67 * <h2>Becoming the {@link CallScreeningService}</h2>
68 * Telecom will bind to a single app chosen by the user which implements the
69 * {@link CallScreeningService} API when there are new incoming and outgoing calls.
70 * <p>
71 * The code snippet below illustrates how your app can request that it fills the call screening
72 * role.
73 * <pre>
74 * {@code
75 * private static final int REQUEST_ID = 1;
76 *
77 * public void requestRole() {
78 * RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
Grace Jia2d21e952019-09-20 14:57:00 -070079 * Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING);
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080080 * startActivityForResult(intent, REQUEST_ID);
81 * }
82 *
83 * &#64;Override
84 * public void onActivityResult(int requestCode, int resultCode, Intent data) {
85 * if (requestCode == REQUEST_ID) {
86 * if (resultCode == android.app.Activity.RESULT_OK) {
87 * // Your app is now the call screening app
88 * } else {
89 * // Your app is not the call screening app
90 * }
91 * }
92 * }
93 * </pre>
Sailesh Nepal1bef3392016-01-24 18:21:53 -080094 */
95public abstract class CallScreeningService extends Service {
96 /**
97 * The {@link Intent} that must be declared as handled by the service.
98 */
99 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
100 public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
101
102 private static final int MSG_SCREEN_CALL = 1;
103
104 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
105 @Override
106 public void handleMessage(Message msg) {
107 switch (msg.what) {
108 case MSG_SCREEN_CALL:
109 SomeArgs args = (SomeArgs) msg.obj;
110 try {
111 mCallScreeningAdapter = (ICallScreeningAdapter) args.arg1;
Grace Jia90b38042019-11-06 14:12:33 -0800112 Call.Details callDetails = Call.Details
113 .createFromParcelableCall((ParcelableCall) args.arg2);
114 onScreenCall(callDetails);
115 if (callDetails.getCallDirection() == Call.Details.DIRECTION_OUTGOING) {
116 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
117 }
118 } catch (RemoteException e) {
119 Log.w(this, "Exception when screening call: " + e);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800120 } finally {
121 args.recycle();
122 }
123 break;
124 }
125 }
126 };
127
128 private final class CallScreeningBinder extends ICallScreeningService.Stub {
129 @Override
130 public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) {
131 Log.v(this, "screenCall");
132 SomeArgs args = SomeArgs.obtain();
133 args.arg1 = adapter;
134 args.arg2 = call;
135 mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget();
136 }
137 }
138
139 private ICallScreeningAdapter mCallScreeningAdapter;
140
141 /*
142 * Information about how to respond to an incoming call.
143 */
Sailesh Nepalf4460712016-01-27 16:45:51 -0800144 public static class CallResponse {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800145 private final boolean mShouldDisallowCall;
146 private final boolean mShouldRejectCall;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800147 private final boolean mShouldSilenceCall;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800148 private final boolean mShouldSkipCallLog;
149 private final boolean mShouldSkipNotification;
Hall Liu69554cf2019-11-11 17:44:09 -0800150 private final boolean mShouldScreenCallViaAudioProcessing;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800151
152 private CallResponse(
153 boolean shouldDisallowCall,
154 boolean shouldRejectCall,
Usman Abdullah47b392d2019-03-06 15:54:56 -0800155 boolean shouldSilenceCall,
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800156 boolean shouldSkipCallLog,
Hall Liu6dfa2492019-10-01 17:20:39 -0700157 boolean shouldSkipNotification,
Hall Liu69554cf2019-11-11 17:44:09 -0800158 boolean shouldScreenCallViaAudioProcessing) {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800159 if (!shouldDisallowCall
160 && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
161 throw new IllegalStateException("Invalid response state for allowed call.");
162 }
163
Hall Liu69554cf2019-11-11 17:44:09 -0800164 if (shouldDisallowCall && shouldScreenCallViaAudioProcessing) {
Hall Liu6dfa2492019-10-01 17:20:39 -0700165 throw new IllegalStateException("Invalid response state for allowed call.");
166 }
167
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800168 mShouldDisallowCall = shouldDisallowCall;
169 mShouldRejectCall = shouldRejectCall;
170 mShouldSkipCallLog = shouldSkipCallLog;
171 mShouldSkipNotification = shouldSkipNotification;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800172 mShouldSilenceCall = shouldSilenceCall;
Hall Liu69554cf2019-11-11 17:44:09 -0800173 mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800174 }
175
176 /*
177 * @return Whether the incoming call should be blocked.
178 */
179 public boolean getDisallowCall() {
180 return mShouldDisallowCall;
181 }
182
183 /*
184 * @return Whether the incoming call should be disconnected as if the user had manually
185 * rejected it.
186 */
187 public boolean getRejectCall() {
188 return mShouldRejectCall;
189 }
190
191 /*
Usman Abdullah47b392d2019-03-06 15:54:56 -0800192 * @return Whether the ringtone should be silenced for the incoming call.
193 */
194 public boolean getSilenceCall() {
195 return mShouldSilenceCall;
196 }
197
198 /*
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800199 * @return Whether the incoming call should not be displayed in the call log.
200 */
201 public boolean getSkipCallLog() {
202 return mShouldSkipCallLog;
203 }
204
205 /*
206 * @return Whether a missed call notification should not be shown for the incoming call.
207 */
208 public boolean getSkipNotification() {
209 return mShouldSkipNotification;
210 }
211
Hall Liu6dfa2492019-10-01 17:20:39 -0700212 /**
213 * @return Whether we should enter the {@link Call#STATE_AUDIO_PROCESSING} state to allow
214 * for further screening of the call.
215 * @hide
216 */
Hall Liu69554cf2019-11-11 17:44:09 -0800217 public boolean getShouldScreenCallViaAudioProcessing() {
218 return mShouldScreenCallViaAudioProcessing;
Hall Liu6dfa2492019-10-01 17:20:39 -0700219 }
220
Sailesh Nepalf4460712016-01-27 16:45:51 -0800221 public static class Builder {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800222 private boolean mShouldDisallowCall;
223 private boolean mShouldRejectCall;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800224 private boolean mShouldSilenceCall;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800225 private boolean mShouldSkipCallLog;
226 private boolean mShouldSkipNotification;
Hall Liu69554cf2019-11-11 17:44:09 -0800227 private boolean mShouldScreenCallViaAudioProcessing;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800228
Tyler Gunne0caec72018-11-30 14:21:18 -0800229 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800230 * Sets whether the incoming call should be blocked.
231 */
232 public Builder setDisallowCall(boolean shouldDisallowCall) {
233 mShouldDisallowCall = shouldDisallowCall;
234 return this;
235 }
236
Tyler Gunne0caec72018-11-30 14:21:18 -0800237 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800238 * Sets whether the incoming call should be disconnected as if the user had manually
239 * rejected it. This property should only be set to true if the call is disallowed.
240 */
241 public Builder setRejectCall(boolean shouldRejectCall) {
242 mShouldRejectCall = shouldRejectCall;
243 return this;
244 }
245
Tyler Gunne0caec72018-11-30 14:21:18 -0800246 /**
Usman Abdullah47b392d2019-03-06 15:54:56 -0800247 * Sets whether ringing should be silenced for the incoming call. When set
248 * to {@code true}, the Telecom framework will not play a ringtone for the call.
249 * The call will, however, still be sent to the default dialer app if it is not blocked.
250 * A {@link CallScreeningService} can use this to ensure a potential nuisance call is
251 * still surfaced to the user, but in a less intrusive manner.
252 *
253 * Setting this to true only makes sense when the call has not been disallowed
254 * using {@link #setDisallowCall(boolean)}.
255 */
256 public @NonNull Builder setSilenceCall(boolean shouldSilenceCall) {
257 mShouldSilenceCall = shouldSilenceCall;
258 return this;
259 }
260
261 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800262 * Sets whether the incoming call should not be displayed in the call log. This property
263 * should only be set to true if the call is disallowed.
Tyler Gunne0caec72018-11-30 14:21:18 -0800264 * <p>
265 * Note: Calls will still be logged with type
266 * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
267 * is set.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800268 */
269 public Builder setSkipCallLog(boolean shouldSkipCallLog) {
270 mShouldSkipCallLog = shouldSkipCallLog;
271 return this;
272 }
273
Tyler Gunne0caec72018-11-30 14:21:18 -0800274 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800275 * Sets whether a missed call notification should not be shown for the incoming call.
276 * This property should only be set to true if the call is disallowed.
277 */
278 public Builder setSkipNotification(boolean shouldSkipNotification) {
279 mShouldSkipNotification = shouldSkipNotification;
280 return this;
281 }
282
Hall Liu6dfa2492019-10-01 17:20:39 -0700283 /**
284 * Sets whether to request background audio processing so that the in-call service can
285 * screen the call further. If set to {@code true}, {@link #setDisallowCall} should be
286 * called with {@code false}, and all other parameters in this builder will be ignored.
Tyler Gunn460b7d42020-05-15 10:19:32 -0700287 * <p>
Hall Liu6dfa2492019-10-01 17:20:39 -0700288 * This request will only be honored if the {@link CallScreeningService} shares the same
289 * uid as the default dialer app. Otherwise, the call will go through as usual.
Tyler Gunn460b7d42020-05-15 10:19:32 -0700290 * <p>
291 * Apps built with SDK version {@link android.os.Build.VERSION_CODES#R} or later which
292 * are using the microphone as part of audio processing should specify the
293 * foreground service type using the attribute
294 * {@link android.R.attr#foregroundServiceType} in the {@link CallScreeningService}
295 * service element of the app's manifest file.
296 * The {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE} attribute should be
297 * specified.
298 * @see
299 * <a href="https://developer.android.com/preview/privacy/foreground-service-types">
300 * the Android Developer Site</a> for more information.
Hall Liu6dfa2492019-10-01 17:20:39 -0700301 *
Hall Liu69554cf2019-11-11 17:44:09 -0800302 * @param shouldScreenCallViaAudioProcessing Whether to request further call screening.
Hall Liu6dfa2492019-10-01 17:20:39 -0700303 * @hide
304 */
305 @SystemApi
306 @TestApi
Hall Liuff4a1252020-01-01 16:27:14 -0800307 @RequiresPermission(Manifest.permission.CAPTURE_AUDIO_OUTPUT)
Hall Liu69554cf2019-11-11 17:44:09 -0800308 public @NonNull Builder setShouldScreenCallViaAudioProcessing(
309 boolean shouldScreenCallViaAudioProcessing) {
310 mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
Hall Liu6dfa2492019-10-01 17:20:39 -0700311 return this;
312 }
313
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800314 public CallResponse build() {
315 return new CallResponse(
316 mShouldDisallowCall,
317 mShouldRejectCall,
Usman Abdullah47b392d2019-03-06 15:54:56 -0800318 mShouldSilenceCall,
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800319 mShouldSkipCallLog,
Hall Liu6dfa2492019-10-01 17:20:39 -0700320 mShouldSkipNotification,
Hall Liu69554cf2019-11-11 17:44:09 -0800321 mShouldScreenCallViaAudioProcessing);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800322 }
323 }
324 }
325
326 public CallScreeningService() {
327 }
328
329 @Override
330 public IBinder onBind(Intent intent) {
331 Log.v(this, "onBind");
332 return new CallScreeningBinder();
333 }
334
335 @Override
336 public boolean onUnbind(Intent intent) {
337 Log.v(this, "onUnbind");
338 return false;
339 }
340
341 /**
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800342 * Called when a new incoming or outgoing call is added which is not in the user's contact list.
343 * <p>
344 * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by
345 * calling
346 * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}.
347 * Your app can tell if a call is an incoming call by checking to see if
348 * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}.
349 * <p>
Tyler Gunne0caec72018-11-30 14:21:18 -0800350 * Note: The {@link Call.Details} instance provided to a call screening service will only have
351 * the following properties set. The rest of the {@link Call.Details} properties will be set to
352 * their default value or {@code null}.
353 * <ul>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800354 * <li>{@link Call.Details#getCallDirection()}</li>
Tyler Gunne0caec72018-11-30 14:21:18 -0800355 * <li>{@link Call.Details#getConnectTimeMillis()}</li>
356 * <li>{@link Call.Details#getCreationTimeMillis()}</li>
357 * <li>{@link Call.Details#getHandle()}</li>
358 * <li>{@link Call.Details#getHandlePresentation()}</li>
359 * </ul>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800360 * <p>
361 * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme}
362 * is {@link PhoneAccount#SCHEME_TEL} are passed for call
363 * screening. Further, only calls which are not in the user's contacts are passed for
364 * screening. For outgoing calls, no post-dial digits are passed.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800365 *
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800366 * @param callDetails Information about a new call, see {@link Call.Details}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800367 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800368 public abstract void onScreenCall(@NonNull Call.Details callDetails);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800369
370 /**
Usman Abdullah47b392d2019-03-06 15:54:56 -0800371 * Responds to the given incoming call, either allowing it, silencing it or disallowing it.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800372 * <p>
373 * The {@link CallScreeningService} calls this method to inform the system whether the call
Usman Abdullah47b392d2019-03-06 15:54:56 -0800374 * should be silently blocked or not. In the event that it should not be blocked, it may
375 * also be requested to ring silently.
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800376 * <p>
377 * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is
378 * {@link Call.Details#DIRECTION_INCOMING}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800379 *
380 * @param callDetails The call to allow.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800381 * <p>
382 * Must be the same {@link Call.Details call} which was provided to the
383 * {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800384 * @param response The {@link CallScreeningService.CallResponse} which contains information
385 * about how to respond to a call.
386 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800387 public final void respondToCall(@NonNull Call.Details callDetails,
388 @NonNull CallResponse response) {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800389 try {
390 if (response.getDisallowCall()) {
391 mCallScreeningAdapter.disallowCall(
392 callDetails.getTelecomCallId(),
393 response.getRejectCall(),
394 !response.getSkipCallLog(),
tonyzhu9e1d4f82018-10-22 15:11:31 +0800395 !response.getSkipNotification(),
396 new ComponentName(getPackageName(), getClass().getName()));
Usman Abdullah47b392d2019-03-06 15:54:56 -0800397 } else if (response.getSilenceCall()) {
398 mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId());
Hall Liu69554cf2019-11-11 17:44:09 -0800399 } else if (response.getShouldScreenCallViaAudioProcessing()) {
Hall Liu31de23d2019-10-11 15:38:29 -0700400 mCallScreeningAdapter.screenCallFurther(callDetails.getTelecomCallId());
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800401 } else {
402 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
403 }
404 } catch (RemoteException e) {
405 }
406 }
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800407}