blob: 8abab90f775f2b07c87a8078b6c6851b85610b52 [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
Tyler Gunn7e45b722018-12-04 12:56:45 -080019import android.annotation.NonNull;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080020import android.annotation.SdkConstant;
Hall Liu6dfa2492019-10-01 17:20:39 -070021import android.annotation.SystemApi;
22import android.annotation.TestApi;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080023import android.app.Service;
tonyzhu9e1d4f82018-10-22 15:11:31 +080024import android.content.ComponentName;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080025import android.content.Intent;
Tyler Gunn460b7d42020-05-15 10:19:32 -070026import android.content.pm.ServiceInfo;
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080027import android.net.Uri;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080028import android.os.Handler;
29import android.os.IBinder;
30import android.os.Looper;
31import android.os.Message;
32import android.os.RemoteException;
33
34import com.android.internal.os.SomeArgs;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080035import com.android.internal.telecom.ICallScreeningAdapter;
Tyler Gunne0caec72018-11-30 14:21:18 -080036import com.android.internal.telecom.ICallScreeningService;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080037
38/**
39 * This service can be implemented by the default dialer (see
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080040 * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
Tyler Gunna842e7622019-03-29 11:32:08 -070041 * incoming calls before they are shown to a user. A {@link CallScreeningService} can also see
42 * outgoing calls for the purpose of providing caller ID services for those calls.
Sailesh Nepal1bef3392016-01-24 18:21:53 -080043 * <p>
44 * Below is an example manifest registration for a {@code CallScreeningService}.
45 * <pre>
46 * {@code
47 * <service android:name="your.package.YourCallScreeningServiceImplementation"
48 * android:permission="android.permission.BIND_SCREENING_SERVICE">
49 * <intent-filter>
50 * <action android:name="android.telecom.CallScreeningService"/>
51 * </intent-filter>
52 * </service>
53 * }
54 * </pre>
Tyler Gunn7e45b722018-12-04 12:56:45 -080055 * <p>
56 * A CallScreeningService performs two functions:
57 * <ol>
58 * <li>Call blocking/screening - the service can choose which calls will ring on the user's
59 * device, and which will be silently sent to voicemail.</li>
Tyler Gunna842e7622019-03-29 11:32:08 -070060 * <li>Call identification - services which provide call identification functionality can
61 * display a user-interface of their choosing which contains identifying information for a call.
62 * </li>
Tyler Gunn7e45b722018-12-04 12:56:45 -080063 * </ol>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080064 * <p>
65 * <h2>Becoming the {@link CallScreeningService}</h2>
66 * Telecom will bind to a single app chosen by the user which implements the
67 * {@link CallScreeningService} API when there are new incoming and outgoing calls.
68 * <p>
69 * The code snippet below illustrates how your app can request that it fills the call screening
70 * role.
71 * <pre>
72 * {@code
73 * private static final int REQUEST_ID = 1;
74 *
75 * public void requestRole() {
76 * RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
Grace Jia2d21e952019-09-20 14:57:00 -070077 * Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING);
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080078 * startActivityForResult(intent, REQUEST_ID);
79 * }
80 *
81 * &#64;Override
82 * public void onActivityResult(int requestCode, int resultCode, Intent data) {
83 * if (requestCode == REQUEST_ID) {
84 * if (resultCode == android.app.Activity.RESULT_OK) {
85 * // Your app is now the call screening app
86 * } else {
87 * // Your app is not the call screening app
88 * }
89 * }
90 * }
91 * </pre>
Sailesh Nepal1bef3392016-01-24 18:21:53 -080092 */
93public abstract class CallScreeningService extends Service {
94 /**
95 * The {@link Intent} that must be declared as handled by the service.
96 */
97 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
98 public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
99
100 private static final int MSG_SCREEN_CALL = 1;
101
102 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
103 @Override
104 public void handleMessage(Message msg) {
105 switch (msg.what) {
106 case MSG_SCREEN_CALL:
107 SomeArgs args = (SomeArgs) msg.obj;
108 try {
109 mCallScreeningAdapter = (ICallScreeningAdapter) args.arg1;
Grace Jia90b38042019-11-06 14:12:33 -0800110 Call.Details callDetails = Call.Details
111 .createFromParcelableCall((ParcelableCall) args.arg2);
112 onScreenCall(callDetails);
113 if (callDetails.getCallDirection() == Call.Details.DIRECTION_OUTGOING) {
114 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
115 }
116 } catch (RemoteException e) {
117 Log.w(this, "Exception when screening call: " + e);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800118 } finally {
119 args.recycle();
120 }
121 break;
122 }
123 }
124 };
125
126 private final class CallScreeningBinder extends ICallScreeningService.Stub {
127 @Override
128 public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) {
129 Log.v(this, "screenCall");
130 SomeArgs args = SomeArgs.obtain();
131 args.arg1 = adapter;
132 args.arg2 = call;
133 mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget();
134 }
135 }
136
137 private ICallScreeningAdapter mCallScreeningAdapter;
138
139 /*
140 * Information about how to respond to an incoming call.
141 */
Sailesh Nepalf4460712016-01-27 16:45:51 -0800142 public static class CallResponse {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800143 private final boolean mShouldDisallowCall;
144 private final boolean mShouldRejectCall;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800145 private final boolean mShouldSilenceCall;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800146 private final boolean mShouldSkipCallLog;
147 private final boolean mShouldSkipNotification;
Hall Liu69554cf2019-11-11 17:44:09 -0800148 private final boolean mShouldScreenCallViaAudioProcessing;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800149
150 private CallResponse(
151 boolean shouldDisallowCall,
152 boolean shouldRejectCall,
Usman Abdullah47b392d2019-03-06 15:54:56 -0800153 boolean shouldSilenceCall,
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800154 boolean shouldSkipCallLog,
Hall Liu6dfa2492019-10-01 17:20:39 -0700155 boolean shouldSkipNotification,
Hall Liu69554cf2019-11-11 17:44:09 -0800156 boolean shouldScreenCallViaAudioProcessing) {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800157 if (!shouldDisallowCall
158 && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
159 throw new IllegalStateException("Invalid response state for allowed call.");
160 }
161
Hall Liu69554cf2019-11-11 17:44:09 -0800162 if (shouldDisallowCall && shouldScreenCallViaAudioProcessing) {
Hall Liu6dfa2492019-10-01 17:20:39 -0700163 throw new IllegalStateException("Invalid response state for allowed call.");
164 }
165
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800166 mShouldDisallowCall = shouldDisallowCall;
167 mShouldRejectCall = shouldRejectCall;
168 mShouldSkipCallLog = shouldSkipCallLog;
169 mShouldSkipNotification = shouldSkipNotification;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800170 mShouldSilenceCall = shouldSilenceCall;
Hall Liu69554cf2019-11-11 17:44:09 -0800171 mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800172 }
173
174 /*
175 * @return Whether the incoming call should be blocked.
176 */
177 public boolean getDisallowCall() {
178 return mShouldDisallowCall;
179 }
180
181 /*
182 * @return Whether the incoming call should be disconnected as if the user had manually
183 * rejected it.
184 */
185 public boolean getRejectCall() {
186 return mShouldRejectCall;
187 }
188
189 /*
Usman Abdullah47b392d2019-03-06 15:54:56 -0800190 * @return Whether the ringtone should be silenced for the incoming call.
191 */
192 public boolean getSilenceCall() {
193 return mShouldSilenceCall;
194 }
195
196 /*
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800197 * @return Whether the incoming call should not be displayed in the call log.
198 */
199 public boolean getSkipCallLog() {
200 return mShouldSkipCallLog;
201 }
202
203 /*
204 * @return Whether a missed call notification should not be shown for the incoming call.
205 */
206 public boolean getSkipNotification() {
207 return mShouldSkipNotification;
208 }
209
Hall Liu6dfa2492019-10-01 17:20:39 -0700210 /**
211 * @return Whether we should enter the {@link Call#STATE_AUDIO_PROCESSING} state to allow
212 * for further screening of the call.
213 * @hide
214 */
Hall Liu69554cf2019-11-11 17:44:09 -0800215 public boolean getShouldScreenCallViaAudioProcessing() {
216 return mShouldScreenCallViaAudioProcessing;
Hall Liu6dfa2492019-10-01 17:20:39 -0700217 }
218
Sailesh Nepalf4460712016-01-27 16:45:51 -0800219 public static class Builder {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800220 private boolean mShouldDisallowCall;
221 private boolean mShouldRejectCall;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800222 private boolean mShouldSilenceCall;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800223 private boolean mShouldSkipCallLog;
224 private boolean mShouldSkipNotification;
Hall Liu69554cf2019-11-11 17:44:09 -0800225 private boolean mShouldScreenCallViaAudioProcessing;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800226
Tyler Gunne0caec72018-11-30 14:21:18 -0800227 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800228 * Sets whether the incoming call should be blocked.
229 */
230 public Builder setDisallowCall(boolean shouldDisallowCall) {
231 mShouldDisallowCall = shouldDisallowCall;
232 return this;
233 }
234
Tyler Gunne0caec72018-11-30 14:21:18 -0800235 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800236 * Sets whether the incoming call should be disconnected as if the user had manually
237 * rejected it. This property should only be set to true if the call is disallowed.
238 */
239 public Builder setRejectCall(boolean shouldRejectCall) {
240 mShouldRejectCall = shouldRejectCall;
241 return this;
242 }
243
Tyler Gunne0caec72018-11-30 14:21:18 -0800244 /**
Usman Abdullah47b392d2019-03-06 15:54:56 -0800245 * Sets whether ringing should be silenced for the incoming call. When set
246 * to {@code true}, the Telecom framework will not play a ringtone for the call.
247 * The call will, however, still be sent to the default dialer app if it is not blocked.
248 * A {@link CallScreeningService} can use this to ensure a potential nuisance call is
249 * still surfaced to the user, but in a less intrusive manner.
250 *
251 * Setting this to true only makes sense when the call has not been disallowed
252 * using {@link #setDisallowCall(boolean)}.
253 */
254 public @NonNull Builder setSilenceCall(boolean shouldSilenceCall) {
255 mShouldSilenceCall = shouldSilenceCall;
256 return this;
257 }
258
259 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800260 * Sets whether the incoming call should not be displayed in the call log. This property
261 * should only be set to true if the call is disallowed.
Tyler Gunne0caec72018-11-30 14:21:18 -0800262 * <p>
263 * Note: Calls will still be logged with type
264 * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
265 * is set.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800266 */
267 public Builder setSkipCallLog(boolean shouldSkipCallLog) {
268 mShouldSkipCallLog = shouldSkipCallLog;
269 return this;
270 }
271
Tyler Gunne0caec72018-11-30 14:21:18 -0800272 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800273 * Sets whether a missed call notification should not be shown for the incoming call.
274 * This property should only be set to true if the call is disallowed.
275 */
276 public Builder setSkipNotification(boolean shouldSkipNotification) {
277 mShouldSkipNotification = shouldSkipNotification;
278 return this;
279 }
280
Hall Liu6dfa2492019-10-01 17:20:39 -0700281 /**
282 * Sets whether to request background audio processing so that the in-call service can
283 * screen the call further. If set to {@code true}, {@link #setDisallowCall} should be
284 * called with {@code false}, and all other parameters in this builder will be ignored.
Tyler Gunn460b7d42020-05-15 10:19:32 -0700285 * <p>
Hall Liu6dfa2492019-10-01 17:20:39 -0700286 * This request will only be honored if the {@link CallScreeningService} shares the same
287 * uid as the default dialer app. Otherwise, the call will go through as usual.
Tyler Gunn460b7d42020-05-15 10:19:32 -0700288 * <p>
289 * Apps built with SDK version {@link android.os.Build.VERSION_CODES#R} or later which
290 * are using the microphone as part of audio processing should specify the
291 * foreground service type using the attribute
292 * {@link android.R.attr#foregroundServiceType} in the {@link CallScreeningService}
293 * service element of the app's manifest file.
294 * The {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE} attribute should be
295 * specified.
296 * @see
297 * <a href="https://developer.android.com/preview/privacy/foreground-service-types">
298 * the Android Developer Site</a> for more information.
Hall Liu6dfa2492019-10-01 17:20:39 -0700299 *
Hall Liu69554cf2019-11-11 17:44:09 -0800300 * @param shouldScreenCallViaAudioProcessing Whether to request further call screening.
Hall Liu6dfa2492019-10-01 17:20:39 -0700301 * @hide
302 */
303 @SystemApi
304 @TestApi
Hall Liu69554cf2019-11-11 17:44:09 -0800305 public @NonNull Builder setShouldScreenCallViaAudioProcessing(
306 boolean shouldScreenCallViaAudioProcessing) {
307 mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
Hall Liu6dfa2492019-10-01 17:20:39 -0700308 return this;
309 }
310
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800311 public CallResponse build() {
312 return new CallResponse(
313 mShouldDisallowCall,
314 mShouldRejectCall,
Usman Abdullah47b392d2019-03-06 15:54:56 -0800315 mShouldSilenceCall,
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800316 mShouldSkipCallLog,
Hall Liu6dfa2492019-10-01 17:20:39 -0700317 mShouldSkipNotification,
Hall Liu69554cf2019-11-11 17:44:09 -0800318 mShouldScreenCallViaAudioProcessing);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800319 }
320 }
321 }
322
323 public CallScreeningService() {
324 }
325
326 @Override
327 public IBinder onBind(Intent intent) {
328 Log.v(this, "onBind");
329 return new CallScreeningBinder();
330 }
331
332 @Override
333 public boolean onUnbind(Intent intent) {
334 Log.v(this, "onUnbind");
335 return false;
336 }
337
338 /**
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800339 * Called when a new incoming or outgoing call is added which is not in the user's contact list.
340 * <p>
341 * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by
342 * calling
343 * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}.
344 * Your app can tell if a call is an incoming call by checking to see if
345 * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}.
346 * <p>
Tyler Gunne0caec72018-11-30 14:21:18 -0800347 * Note: The {@link Call.Details} instance provided to a call screening service will only have
348 * the following properties set. The rest of the {@link Call.Details} properties will be set to
349 * their default value or {@code null}.
350 * <ul>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800351 * <li>{@link Call.Details#getCallDirection()}</li>
Tyler Gunne0caec72018-11-30 14:21:18 -0800352 * <li>{@link Call.Details#getConnectTimeMillis()}</li>
353 * <li>{@link Call.Details#getCreationTimeMillis()}</li>
354 * <li>{@link Call.Details#getHandle()}</li>
355 * <li>{@link Call.Details#getHandlePresentation()}</li>
356 * </ul>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800357 * <p>
358 * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme}
359 * is {@link PhoneAccount#SCHEME_TEL} are passed for call
360 * screening. Further, only calls which are not in the user's contacts are passed for
361 * screening. For outgoing calls, no post-dial digits are passed.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800362 *
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800363 * @param callDetails Information about a new call, see {@link Call.Details}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800364 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800365 public abstract void onScreenCall(@NonNull Call.Details callDetails);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800366
367 /**
Usman Abdullah47b392d2019-03-06 15:54:56 -0800368 * Responds to the given incoming call, either allowing it, silencing it or disallowing it.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800369 * <p>
370 * The {@link CallScreeningService} calls this method to inform the system whether the call
Usman Abdullah47b392d2019-03-06 15:54:56 -0800371 * should be silently blocked or not. In the event that it should not be blocked, it may
372 * also be requested to ring silently.
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800373 * <p>
374 * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is
375 * {@link Call.Details#DIRECTION_INCOMING}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800376 *
377 * @param callDetails The call to allow.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800378 * <p>
379 * Must be the same {@link Call.Details call} which was provided to the
380 * {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800381 * @param response The {@link CallScreeningService.CallResponse} which contains information
382 * about how to respond to a call.
383 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800384 public final void respondToCall(@NonNull Call.Details callDetails,
385 @NonNull CallResponse response) {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800386 try {
387 if (response.getDisallowCall()) {
388 mCallScreeningAdapter.disallowCall(
389 callDetails.getTelecomCallId(),
390 response.getRejectCall(),
391 !response.getSkipCallLog(),
tonyzhu9e1d4f82018-10-22 15:11:31 +0800392 !response.getSkipNotification(),
393 new ComponentName(getPackageName(), getClass().getName()));
Usman Abdullah47b392d2019-03-06 15:54:56 -0800394 } else if (response.getSilenceCall()) {
395 mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId());
Hall Liu69554cf2019-11-11 17:44:09 -0800396 } else if (response.getShouldScreenCallViaAudioProcessing()) {
Hall Liu31de23d2019-10-11 15:38:29 -0700397 mCallScreeningAdapter.screenCallFurther(callDetails.getTelecomCallId());
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800398 } else {
399 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
400 }
401 } catch (RemoteException e) {
402 }
403 }
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800404}