blob: f8722f4ea653d714095ecdd94cce691f37674598 [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 Gunn9e76fd12018-12-17 09:56:11 -080026import android.net.Uri;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080027import android.os.Handler;
28import android.os.IBinder;
29import android.os.Looper;
30import android.os.Message;
31import android.os.RemoteException;
32
33import com.android.internal.os.SomeArgs;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080034import com.android.internal.telecom.ICallScreeningAdapter;
Tyler Gunne0caec72018-11-30 14:21:18 -080035import com.android.internal.telecom.ICallScreeningService;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080036
37/**
38 * This service can be implemented by the default dialer (see
Tyler Gunn9e76fd12018-12-17 09:56:11 -080039 * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
Tyler Gunna842e762019-03-29 11:32:08 -070040 * incoming calls before they are shown to a user. A {@link CallScreeningService} can also see
41 * outgoing calls for the purpose of providing caller ID services for those calls.
Sailesh Nepal1bef3392016-01-24 18:21:53 -080042 * <p>
43 * Below is an example manifest registration for a {@code CallScreeningService}.
44 * <pre>
45 * {@code
46 * <service android:name="your.package.YourCallScreeningServiceImplementation"
47 * android:permission="android.permission.BIND_SCREENING_SERVICE">
48 * <intent-filter>
49 * <action android:name="android.telecom.CallScreeningService"/>
50 * </intent-filter>
51 * </service>
52 * }
53 * </pre>
Tyler Gunn7e45b722018-12-04 12:56:45 -080054 * <p>
55 * A CallScreeningService performs two functions:
56 * <ol>
57 * <li>Call blocking/screening - the service can choose which calls will ring on the user's
58 * device, and which will be silently sent to voicemail.</li>
Tyler Gunna842e762019-03-29 11:32:08 -070059 * <li>Call identification - services which provide call identification functionality can
60 * display a user-interface of their choosing which contains identifying information for a call.
61 * </li>
Tyler Gunn7e45b722018-12-04 12:56:45 -080062 * </ol>
Tyler Gunn9e76fd12018-12-17 09:56:11 -080063 * <p>
64 * <h2>Becoming the {@link CallScreeningService}</h2>
65 * Telecom will bind to a single app chosen by the user which implements the
66 * {@link CallScreeningService} API when there are new incoming and outgoing calls.
67 * <p>
68 * The code snippet below illustrates how your app can request that it fills the call screening
69 * role.
70 * <pre>
71 * {@code
72 * private static final int REQUEST_ID = 1;
73 *
74 * public void requestRole() {
75 * RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
Grace Jia2d21e952019-09-20 14:57:00 -070076 * Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING);
Tyler Gunn9e76fd12018-12-17 09:56:11 -080077 * startActivityForResult(intent, REQUEST_ID);
78 * }
79 *
80 * &#64;Override
81 * public void onActivityResult(int requestCode, int resultCode, Intent data) {
82 * if (requestCode == REQUEST_ID) {
83 * if (resultCode == android.app.Activity.RESULT_OK) {
84 * // Your app is now the call screening app
85 * } else {
86 * // Your app is not the call screening app
87 * }
88 * }
89 * }
90 * </pre>
Sailesh Nepal1bef3392016-01-24 18:21:53 -080091 */
92public abstract class CallScreeningService extends Service {
93 /**
94 * The {@link Intent} that must be declared as handled by the service.
95 */
96 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
97 public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
98
99 private static final int MSG_SCREEN_CALL = 1;
100
101 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
102 @Override
103 public void handleMessage(Message msg) {
104 switch (msg.what) {
105 case MSG_SCREEN_CALL:
106 SomeArgs args = (SomeArgs) msg.obj;
107 try {
108 mCallScreeningAdapter = (ICallScreeningAdapter) args.arg1;
Grace Jia90b38042019-11-06 14:12:33 -0800109 Call.Details callDetails = Call.Details
110 .createFromParcelableCall((ParcelableCall) args.arg2);
111 onScreenCall(callDetails);
112 if (callDetails.getCallDirection() == Call.Details.DIRECTION_OUTGOING) {
113 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
114 }
115 } catch (RemoteException e) {
116 Log.w(this, "Exception when screening call: " + e);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800117 } finally {
118 args.recycle();
119 }
120 break;
121 }
122 }
123 };
124
125 private final class CallScreeningBinder extends ICallScreeningService.Stub {
126 @Override
127 public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) {
128 Log.v(this, "screenCall");
129 SomeArgs args = SomeArgs.obtain();
130 args.arg1 = adapter;
131 args.arg2 = call;
132 mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget();
133 }
134 }
135
136 private ICallScreeningAdapter mCallScreeningAdapter;
137
138 /*
139 * Information about how to respond to an incoming call.
140 */
Sailesh Nepalf4460712016-01-27 16:45:51 -0800141 public static class CallResponse {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800142 private final boolean mShouldDisallowCall;
143 private final boolean mShouldRejectCall;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800144 private final boolean mShouldSilenceCall;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800145 private final boolean mShouldSkipCallLog;
146 private final boolean mShouldSkipNotification;
Hall Liu69554cf2019-11-11 17:44:09 -0800147 private final boolean mShouldScreenCallViaAudioProcessing;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800148
149 private CallResponse(
150 boolean shouldDisallowCall,
151 boolean shouldRejectCall,
Usman Abdullah47b392d2019-03-06 15:54:56 -0800152 boolean shouldSilenceCall,
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800153 boolean shouldSkipCallLog,
Hall Liu6dfa2492019-10-01 17:20:39 -0700154 boolean shouldSkipNotification,
Hall Liu69554cf2019-11-11 17:44:09 -0800155 boolean shouldScreenCallViaAudioProcessing) {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800156 if (!shouldDisallowCall
157 && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
158 throw new IllegalStateException("Invalid response state for allowed call.");
159 }
160
Hall Liu69554cf2019-11-11 17:44:09 -0800161 if (shouldDisallowCall && shouldScreenCallViaAudioProcessing) {
Hall Liu6dfa2492019-10-01 17:20:39 -0700162 throw new IllegalStateException("Invalid response state for allowed call.");
163 }
164
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800165 mShouldDisallowCall = shouldDisallowCall;
166 mShouldRejectCall = shouldRejectCall;
167 mShouldSkipCallLog = shouldSkipCallLog;
168 mShouldSkipNotification = shouldSkipNotification;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800169 mShouldSilenceCall = shouldSilenceCall;
Hall Liu69554cf2019-11-11 17:44:09 -0800170 mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800171 }
172
173 /*
174 * @return Whether the incoming call should be blocked.
175 */
176 public boolean getDisallowCall() {
177 return mShouldDisallowCall;
178 }
179
180 /*
181 * @return Whether the incoming call should be disconnected as if the user had manually
182 * rejected it.
183 */
184 public boolean getRejectCall() {
185 return mShouldRejectCall;
186 }
187
188 /*
Usman Abdullah47b392d2019-03-06 15:54:56 -0800189 * @return Whether the ringtone should be silenced for the incoming call.
190 */
191 public boolean getSilenceCall() {
192 return mShouldSilenceCall;
193 }
194
195 /*
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800196 * @return Whether the incoming call should not be displayed in the call log.
197 */
198 public boolean getSkipCallLog() {
199 return mShouldSkipCallLog;
200 }
201
202 /*
203 * @return Whether a missed call notification should not be shown for the incoming call.
204 */
205 public boolean getSkipNotification() {
206 return mShouldSkipNotification;
207 }
208
Hall Liu6dfa2492019-10-01 17:20:39 -0700209 /**
210 * @return Whether we should enter the {@link Call#STATE_AUDIO_PROCESSING} state to allow
211 * for further screening of the call.
212 * @hide
213 */
Hall Liu69554cf2019-11-11 17:44:09 -0800214 public boolean getShouldScreenCallViaAudioProcessing() {
215 return mShouldScreenCallViaAudioProcessing;
Hall Liu6dfa2492019-10-01 17:20:39 -0700216 }
217
Sailesh Nepalf4460712016-01-27 16:45:51 -0800218 public static class Builder {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800219 private boolean mShouldDisallowCall;
220 private boolean mShouldRejectCall;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800221 private boolean mShouldSilenceCall;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800222 private boolean mShouldSkipCallLog;
223 private boolean mShouldSkipNotification;
Hall Liu69554cf2019-11-11 17:44:09 -0800224 private boolean mShouldScreenCallViaAudioProcessing;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800225
Tyler Gunne0caec72018-11-30 14:21:18 -0800226 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800227 * Sets whether the incoming call should be blocked.
228 */
229 public Builder setDisallowCall(boolean shouldDisallowCall) {
230 mShouldDisallowCall = shouldDisallowCall;
231 return this;
232 }
233
Tyler Gunne0caec72018-11-30 14:21:18 -0800234 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800235 * Sets whether the incoming call should be disconnected as if the user had manually
236 * rejected it. This property should only be set to true if the call is disallowed.
237 */
238 public Builder setRejectCall(boolean shouldRejectCall) {
239 mShouldRejectCall = shouldRejectCall;
240 return this;
241 }
242
Tyler Gunne0caec72018-11-30 14:21:18 -0800243 /**
Usman Abdullah47b392d2019-03-06 15:54:56 -0800244 * Sets whether ringing should be silenced for the incoming call. When set
245 * to {@code true}, the Telecom framework will not play a ringtone for the call.
246 * The call will, however, still be sent to the default dialer app if it is not blocked.
247 * A {@link CallScreeningService} can use this to ensure a potential nuisance call is
248 * still surfaced to the user, but in a less intrusive manner.
249 *
250 * Setting this to true only makes sense when the call has not been disallowed
251 * using {@link #setDisallowCall(boolean)}.
252 */
253 public @NonNull Builder setSilenceCall(boolean shouldSilenceCall) {
254 mShouldSilenceCall = shouldSilenceCall;
255 return this;
256 }
257
258 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800259 * Sets whether the incoming call should not be displayed in the call log. This property
260 * should only be set to true if the call is disallowed.
Tyler Gunne0caec72018-11-30 14:21:18 -0800261 * <p>
262 * Note: Calls will still be logged with type
263 * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
264 * is set.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800265 */
266 public Builder setSkipCallLog(boolean shouldSkipCallLog) {
267 mShouldSkipCallLog = shouldSkipCallLog;
268 return this;
269 }
270
Tyler Gunne0caec72018-11-30 14:21:18 -0800271 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800272 * Sets whether a missed call notification should not be shown for the incoming call.
273 * This property should only be set to true if the call is disallowed.
274 */
275 public Builder setSkipNotification(boolean shouldSkipNotification) {
276 mShouldSkipNotification = shouldSkipNotification;
277 return this;
278 }
279
Hall Liu6dfa2492019-10-01 17:20:39 -0700280 /**
281 * Sets whether to request background audio processing so that the in-call service can
282 * screen the call further. If set to {@code true}, {@link #setDisallowCall} should be
283 * called with {@code false}, and all other parameters in this builder will be ignored.
284 *
285 * This request will only be honored if the {@link CallScreeningService} shares the same
286 * uid as the default dialer app. Otherwise, the call will go through as usual.
287 *
Hall Liu69554cf2019-11-11 17:44:09 -0800288 * @param shouldScreenCallViaAudioProcessing Whether to request further call screening.
Hall Liu6dfa2492019-10-01 17:20:39 -0700289 * @hide
290 */
291 @SystemApi
292 @TestApi
Hall Liu69554cf2019-11-11 17:44:09 -0800293 public @NonNull Builder setShouldScreenCallViaAudioProcessing(
294 boolean shouldScreenCallViaAudioProcessing) {
295 mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
Hall Liu6dfa2492019-10-01 17:20:39 -0700296 return this;
297 }
298
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800299 public CallResponse build() {
300 return new CallResponse(
301 mShouldDisallowCall,
302 mShouldRejectCall,
Usman Abdullah47b392d2019-03-06 15:54:56 -0800303 mShouldSilenceCall,
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800304 mShouldSkipCallLog,
Hall Liu6dfa2492019-10-01 17:20:39 -0700305 mShouldSkipNotification,
Hall Liu69554cf2019-11-11 17:44:09 -0800306 mShouldScreenCallViaAudioProcessing);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800307 }
308 }
309 }
310
311 public CallScreeningService() {
312 }
313
314 @Override
315 public IBinder onBind(Intent intent) {
316 Log.v(this, "onBind");
317 return new CallScreeningBinder();
318 }
319
320 @Override
321 public boolean onUnbind(Intent intent) {
322 Log.v(this, "onUnbind");
323 return false;
324 }
325
326 /**
Tyler Gunn9e76fd12018-12-17 09:56:11 -0800327 * Called when a new incoming or outgoing call is added which is not in the user's contact list.
328 * <p>
329 * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by
330 * calling
331 * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}.
332 * Your app can tell if a call is an incoming call by checking to see if
333 * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}.
334 * <p>
Tyler Gunne0caec72018-11-30 14:21:18 -0800335 * Note: The {@link Call.Details} instance provided to a call screening service will only have
336 * the following properties set. The rest of the {@link Call.Details} properties will be set to
337 * their default value or {@code null}.
338 * <ul>
Tyler Gunn9e76fd12018-12-17 09:56:11 -0800339 * <li>{@link Call.Details#getCallDirection()}</li>
Tyler Gunne0caec72018-11-30 14:21:18 -0800340 * <li>{@link Call.Details#getConnectTimeMillis()}</li>
341 * <li>{@link Call.Details#getCreationTimeMillis()}</li>
342 * <li>{@link Call.Details#getHandle()}</li>
343 * <li>{@link Call.Details#getHandlePresentation()}</li>
344 * </ul>
Tyler Gunn9e76fd12018-12-17 09:56:11 -0800345 * <p>
346 * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme}
347 * is {@link PhoneAccount#SCHEME_TEL} are passed for call
348 * screening. Further, only calls which are not in the user's contacts are passed for
349 * screening. For outgoing calls, no post-dial digits are passed.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800350 *
Tyler Gunn9e76fd12018-12-17 09:56:11 -0800351 * @param callDetails Information about a new call, see {@link Call.Details}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800352 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800353 public abstract void onScreenCall(@NonNull Call.Details callDetails);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800354
355 /**
Usman Abdullah47b392d2019-03-06 15:54:56 -0800356 * Responds to the given incoming call, either allowing it, silencing it or disallowing it.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800357 * <p>
358 * The {@link CallScreeningService} calls this method to inform the system whether the call
Usman Abdullah47b392d2019-03-06 15:54:56 -0800359 * should be silently blocked or not. In the event that it should not be blocked, it may
360 * also be requested to ring silently.
Tyler Gunn9e76fd12018-12-17 09:56:11 -0800361 * <p>
362 * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is
363 * {@link Call.Details#DIRECTION_INCOMING}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800364 *
365 * @param callDetails The call to allow.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800366 * <p>
367 * Must be the same {@link Call.Details call} which was provided to the
368 * {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800369 * @param response The {@link CallScreeningService.CallResponse} which contains information
370 * about how to respond to a call.
371 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800372 public final void respondToCall(@NonNull Call.Details callDetails,
373 @NonNull CallResponse response) {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800374 try {
375 if (response.getDisallowCall()) {
376 mCallScreeningAdapter.disallowCall(
377 callDetails.getTelecomCallId(),
378 response.getRejectCall(),
379 !response.getSkipCallLog(),
tonyzhu9e1d4f82018-10-22 15:11:31 +0800380 !response.getSkipNotification(),
381 new ComponentName(getPackageName(), getClass().getName()));
Usman Abdullah47b392d2019-03-06 15:54:56 -0800382 } else if (response.getSilenceCall()) {
383 mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId());
Hall Liu69554cf2019-11-11 17:44:09 -0800384 } else if (response.getShouldScreenCallViaAudioProcessing()) {
Hall Liu31de23d2019-10-11 15:38:29 -0700385 mCallScreeningAdapter.screenCallFurther(callDetails.getTelecomCallId());
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800386 } else {
387 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
388 }
389 } catch (RemoteException e) {
390 }
391 }
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800392}