blob: 9016b3e836445fb3be25d4596f0b970be6963e3d [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 Gunn1965bb12019-01-16 10:42:14 -080019import android.annotation.IntDef;
Tyler Gunn7e45b722018-12-04 12:56:45 -080020import android.annotation.NonNull;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080021import android.annotation.SdkConstant;
Hall Liu6dfa2492019-10-01 17:20:39 -070022import android.annotation.SystemApi;
23import android.annotation.TestApi;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080024import android.app.Service;
tonyzhu9e1d4f82018-10-22 15:11:31 +080025import android.content.ComponentName;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080026import android.content.Intent;
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
Tyler Gunn1965bb12019-01-16 10:42:14 -080038import java.lang.annotation.Retention;
39import java.lang.annotation.RetentionPolicy;
40
Sailesh Nepal1bef3392016-01-24 18:21:53 -080041/**
42 * This service can be implemented by the default dialer (see
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080043 * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
Tyler Gunna842e762019-03-29 11:32:08 -070044 * incoming calls before they are shown to a user. A {@link CallScreeningService} can also see
45 * outgoing calls for the purpose of providing caller ID services for those calls.
Sailesh Nepal1bef3392016-01-24 18:21:53 -080046 * <p>
47 * Below is an example manifest registration for a {@code CallScreeningService}.
48 * <pre>
49 * {@code
50 * <service android:name="your.package.YourCallScreeningServiceImplementation"
51 * android:permission="android.permission.BIND_SCREENING_SERVICE">
52 * <intent-filter>
53 * <action android:name="android.telecom.CallScreeningService"/>
54 * </intent-filter>
55 * </service>
56 * }
57 * </pre>
Tyler Gunn7e45b722018-12-04 12:56:45 -080058 * <p>
59 * A CallScreeningService performs two functions:
60 * <ol>
61 * <li>Call blocking/screening - the service can choose which calls will ring on the user's
62 * device, and which will be silently sent to voicemail.</li>
Tyler Gunna842e762019-03-29 11:32:08 -070063 * <li>Call identification - services which provide call identification functionality can
64 * display a user-interface of their choosing which contains identifying information for a call.
65 * </li>
Tyler Gunn7e45b722018-12-04 12:56:45 -080066 * </ol>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080067 * <p>
68 * <h2>Becoming the {@link CallScreeningService}</h2>
69 * Telecom will bind to a single app chosen by the user which implements the
70 * {@link CallScreeningService} API when there are new incoming and outgoing calls.
71 * <p>
72 * The code snippet below illustrates how your app can request that it fills the call screening
73 * role.
74 * <pre>
75 * {@code
76 * private static final int REQUEST_ID = 1;
77 *
78 * public void requestRole() {
79 * RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
Hai Zhangb0146d72019-02-06 16:32:23 -080080 * Intent intent = roleManager.createRequestRoleIntent("android.app.role.CALL_SCREENING");
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080081 * startActivityForResult(intent, REQUEST_ID);
82 * }
83 *
84 * &#64;Override
85 * public void onActivityResult(int requestCode, int resultCode, Intent data) {
86 * if (requestCode == REQUEST_ID) {
87 * if (resultCode == android.app.Activity.RESULT_OK) {
88 * // Your app is now the call screening app
89 * } else {
90 * // Your app is not the call screening app
91 * }
92 * }
93 * }
94 * </pre>
Sailesh Nepal1bef3392016-01-24 18:21:53 -080095 */
96public abstract class CallScreeningService extends Service {
97 /**
98 * The {@link Intent} that must be declared as handled by the service.
99 */
100 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
101 public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
102
103 private static final int MSG_SCREEN_CALL = 1;
104
105 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
106 @Override
107 public void handleMessage(Message msg) {
108 switch (msg.what) {
109 case MSG_SCREEN_CALL:
110 SomeArgs args = (SomeArgs) msg.obj;
111 try {
112 mCallScreeningAdapter = (ICallScreeningAdapter) args.arg1;
113 onScreenCall(
114 Call.Details.createFromParcelableCall((ParcelableCall) args.arg2));
115 } finally {
116 args.recycle();
117 }
118 break;
119 }
120 }
121 };
122
123 private final class CallScreeningBinder extends ICallScreeningService.Stub {
124 @Override
125 public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) {
126 Log.v(this, "screenCall");
127 SomeArgs args = SomeArgs.obtain();
128 args.arg1 = adapter;
129 args.arg2 = call;
130 mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget();
131 }
132 }
133
134 private ICallScreeningAdapter mCallScreeningAdapter;
135
136 /*
137 * Information about how to respond to an incoming call.
138 */
Sailesh Nepalf4460712016-01-27 16:45:51 -0800139 public static class CallResponse {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800140 private final boolean mShouldDisallowCall;
141 private final boolean mShouldRejectCall;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800142 private final boolean mShouldSilenceCall;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800143 private final boolean mShouldSkipCallLog;
144 private final boolean mShouldSkipNotification;
Hall Liu6dfa2492019-10-01 17:20:39 -0700145 private final boolean mShouldScreenCallFurther;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800146
147 private CallResponse(
148 boolean shouldDisallowCall,
149 boolean shouldRejectCall,
Usman Abdullah47b392d2019-03-06 15:54:56 -0800150 boolean shouldSilenceCall,
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800151 boolean shouldSkipCallLog,
Hall Liu6dfa2492019-10-01 17:20:39 -0700152 boolean shouldSkipNotification,
153 boolean shouldScreenCallFurther) {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800154 if (!shouldDisallowCall
155 && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
156 throw new IllegalStateException("Invalid response state for allowed call.");
157 }
158
Hall Liu6dfa2492019-10-01 17:20:39 -0700159 if (shouldDisallowCall && shouldScreenCallFurther) {
160 throw new IllegalStateException("Invalid response state for allowed call.");
161 }
162
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800163 mShouldDisallowCall = shouldDisallowCall;
164 mShouldRejectCall = shouldRejectCall;
165 mShouldSkipCallLog = shouldSkipCallLog;
166 mShouldSkipNotification = shouldSkipNotification;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800167 mShouldSilenceCall = shouldSilenceCall;
Hall Liu6dfa2492019-10-01 17:20:39 -0700168 mShouldScreenCallFurther = shouldScreenCallFurther;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800169 }
170
171 /*
172 * @return Whether the incoming call should be blocked.
173 */
174 public boolean getDisallowCall() {
175 return mShouldDisallowCall;
176 }
177
178 /*
179 * @return Whether the incoming call should be disconnected as if the user had manually
180 * rejected it.
181 */
182 public boolean getRejectCall() {
183 return mShouldRejectCall;
184 }
185
186 /*
Usman Abdullah47b392d2019-03-06 15:54:56 -0800187 * @return Whether the ringtone should be silenced for the incoming call.
188 */
189 public boolean getSilenceCall() {
190 return mShouldSilenceCall;
191 }
192
193 /*
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800194 * @return Whether the incoming call should not be displayed in the call log.
195 */
196 public boolean getSkipCallLog() {
197 return mShouldSkipCallLog;
198 }
199
200 /*
201 * @return Whether a missed call notification should not be shown for the incoming call.
202 */
203 public boolean getSkipNotification() {
204 return mShouldSkipNotification;
205 }
206
Hall Liu6dfa2492019-10-01 17:20:39 -0700207 /**
208 * @return Whether we should enter the {@link Call#STATE_AUDIO_PROCESSING} state to allow
209 * for further screening of the call.
210 * @hide
211 */
212 public boolean getShouldScreenCallFurther() {
213 return mShouldScreenCallFurther;
214 }
215
Sailesh Nepalf4460712016-01-27 16:45:51 -0800216 public static class Builder {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800217 private boolean mShouldDisallowCall;
218 private boolean mShouldRejectCall;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800219 private boolean mShouldSilenceCall;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800220 private boolean mShouldSkipCallLog;
221 private boolean mShouldSkipNotification;
Hall Liu6dfa2492019-10-01 17:20:39 -0700222 private boolean mShouldScreenCallFurther;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800223
Tyler Gunne0caec72018-11-30 14:21:18 -0800224 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800225 * Sets whether the incoming call should be blocked.
226 */
227 public Builder setDisallowCall(boolean shouldDisallowCall) {
228 mShouldDisallowCall = shouldDisallowCall;
229 return this;
230 }
231
Tyler Gunne0caec72018-11-30 14:21:18 -0800232 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800233 * Sets whether the incoming call should be disconnected as if the user had manually
234 * rejected it. This property should only be set to true if the call is disallowed.
235 */
236 public Builder setRejectCall(boolean shouldRejectCall) {
237 mShouldRejectCall = shouldRejectCall;
238 return this;
239 }
240
Tyler Gunne0caec72018-11-30 14:21:18 -0800241 /**
Usman Abdullah47b392d2019-03-06 15:54:56 -0800242 * Sets whether ringing should be silenced for the incoming call. When set
243 * to {@code true}, the Telecom framework will not play a ringtone for the call.
244 * The call will, however, still be sent to the default dialer app if it is not blocked.
245 * A {@link CallScreeningService} can use this to ensure a potential nuisance call is
246 * still surfaced to the user, but in a less intrusive manner.
247 *
248 * Setting this to true only makes sense when the call has not been disallowed
249 * using {@link #setDisallowCall(boolean)}.
250 */
251 public @NonNull Builder setSilenceCall(boolean shouldSilenceCall) {
252 mShouldSilenceCall = shouldSilenceCall;
253 return this;
254 }
255
256 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800257 * Sets whether the incoming call should not be displayed in the call log. This property
258 * should only be set to true if the call is disallowed.
Tyler Gunne0caec72018-11-30 14:21:18 -0800259 * <p>
260 * Note: Calls will still be logged with type
261 * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
262 * is set.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800263 */
264 public Builder setSkipCallLog(boolean shouldSkipCallLog) {
265 mShouldSkipCallLog = shouldSkipCallLog;
266 return this;
267 }
268
Tyler Gunne0caec72018-11-30 14:21:18 -0800269 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800270 * Sets whether a missed call notification should not be shown for the incoming call.
271 * This property should only be set to true if the call is disallowed.
272 */
273 public Builder setSkipNotification(boolean shouldSkipNotification) {
274 mShouldSkipNotification = shouldSkipNotification;
275 return this;
276 }
277
Hall Liu6dfa2492019-10-01 17:20:39 -0700278 /**
279 * Sets whether to request background audio processing so that the in-call service can
280 * screen the call further. If set to {@code true}, {@link #setDisallowCall} should be
281 * called with {@code false}, and all other parameters in this builder will be ignored.
282 *
283 * This request will only be honored if the {@link CallScreeningService} shares the same
284 * uid as the default dialer app. Otherwise, the call will go through as usual.
285 *
286 * @param shouldScreenCallFurther Whether to request further call screening.
287 * @hide
288 */
289 @SystemApi
290 @TestApi
291 public Builder setShouldScreenCallFurther(boolean shouldScreenCallFurther) {
292 mShouldScreenCallFurther = shouldScreenCallFurther;
293 return this;
294 }
295
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800296 public CallResponse build() {
297 return new CallResponse(
298 mShouldDisallowCall,
299 mShouldRejectCall,
Usman Abdullah47b392d2019-03-06 15:54:56 -0800300 mShouldSilenceCall,
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800301 mShouldSkipCallLog,
Hall Liu6dfa2492019-10-01 17:20:39 -0700302 mShouldSkipNotification,
303 mShouldScreenCallFurther);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800304 }
305 }
306 }
307
308 public CallScreeningService() {
309 }
310
311 @Override
312 public IBinder onBind(Intent intent) {
313 Log.v(this, "onBind");
314 return new CallScreeningBinder();
315 }
316
317 @Override
318 public boolean onUnbind(Intent intent) {
319 Log.v(this, "onUnbind");
320 return false;
321 }
322
323 /**
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800324 * Called when a new incoming or outgoing call is added which is not in the user's contact list.
325 * <p>
326 * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by
327 * calling
328 * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}.
329 * Your app can tell if a call is an incoming call by checking to see if
330 * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}.
331 * <p>
Tyler Gunne0caec72018-11-30 14:21:18 -0800332 * Note: The {@link Call.Details} instance provided to a call screening service will only have
333 * the following properties set. The rest of the {@link Call.Details} properties will be set to
334 * their default value or {@code null}.
335 * <ul>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800336 * <li>{@link Call.Details#getCallDirection()}</li>
Tyler Gunne0caec72018-11-30 14:21:18 -0800337 * <li>{@link Call.Details#getConnectTimeMillis()}</li>
338 * <li>{@link Call.Details#getCreationTimeMillis()}</li>
339 * <li>{@link Call.Details#getHandle()}</li>
340 * <li>{@link Call.Details#getHandlePresentation()}</li>
341 * </ul>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800342 * <p>
343 * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme}
344 * is {@link PhoneAccount#SCHEME_TEL} are passed for call
345 * screening. Further, only calls which are not in the user's contacts are passed for
346 * screening. For outgoing calls, no post-dial digits are passed.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800347 *
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800348 * @param callDetails Information about a new call, see {@link Call.Details}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800349 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800350 public abstract void onScreenCall(@NonNull Call.Details callDetails);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800351
352 /**
Usman Abdullah47b392d2019-03-06 15:54:56 -0800353 * Responds to the given incoming call, either allowing it, silencing it or disallowing it.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800354 * <p>
355 * The {@link CallScreeningService} calls this method to inform the system whether the call
Usman Abdullah47b392d2019-03-06 15:54:56 -0800356 * should be silently blocked or not. In the event that it should not be blocked, it may
357 * also be requested to ring silently.
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800358 * <p>
359 * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is
360 * {@link Call.Details#DIRECTION_INCOMING}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800361 *
362 * @param callDetails The call to allow.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800363 * <p>
364 * Must be the same {@link Call.Details call} which was provided to the
365 * {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800366 * @param response The {@link CallScreeningService.CallResponse} which contains information
367 * about how to respond to a call.
368 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800369 public final void respondToCall(@NonNull Call.Details callDetails,
370 @NonNull CallResponse response) {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800371 try {
372 if (response.getDisallowCall()) {
373 mCallScreeningAdapter.disallowCall(
374 callDetails.getTelecomCallId(),
375 response.getRejectCall(),
376 !response.getSkipCallLog(),
tonyzhu9e1d4f82018-10-22 15:11:31 +0800377 !response.getSkipNotification(),
378 new ComponentName(getPackageName(), getClass().getName()));
Usman Abdullah47b392d2019-03-06 15:54:56 -0800379 } else if (response.getSilenceCall()) {
380 mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId());
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800381 } else {
382 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
383 }
384 } catch (RemoteException e) {
385 }
386 }
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800387}