blob: 0e0406d4035b828146ccdcb8c6ca02cc70679614 [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;
22import android.app.Service;
tonyzhu9e1d4f82018-10-22 15:11:31 +080023import android.content.ComponentName;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080024import android.content.Intent;
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -080025import android.net.Uri;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080026import android.os.Handler;
27import android.os.IBinder;
28import android.os.Looper;
29import android.os.Message;
30import android.os.RemoteException;
31
32import com.android.internal.os.SomeArgs;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080033import com.android.internal.telecom.ICallScreeningAdapter;
Tyler Gunne0caec72018-11-30 14:21:18 -080034import com.android.internal.telecom.ICallScreeningService;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080035
Tyler Gunn1965bb12019-01-16 10:42:14 -080036import java.lang.annotation.Retention;
37import java.lang.annotation.RetentionPolicy;
38
Sailesh Nepal1bef3392016-01-24 18:21:53 -080039/**
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 Gunna842e762019-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 Gunna842e762019-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);
Hai Zhangb0146d72019-02-06 16:32:23 -080078 * Intent intent = roleManager.createRequestRoleIntent("android.app.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;
111 onScreenCall(
112 Call.Details.createFromParcelableCall((ParcelableCall) args.arg2));
113 } finally {
114 args.recycle();
115 }
116 break;
117 }
118 }
119 };
120
121 private final class CallScreeningBinder extends ICallScreeningService.Stub {
122 @Override
123 public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) {
124 Log.v(this, "screenCall");
125 SomeArgs args = SomeArgs.obtain();
126 args.arg1 = adapter;
127 args.arg2 = call;
128 mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget();
129 }
130 }
131
132 private ICallScreeningAdapter mCallScreeningAdapter;
133
134 /*
135 * Information about how to respond to an incoming call.
136 */
Sailesh Nepalf4460712016-01-27 16:45:51 -0800137 public static class CallResponse {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800138 private final boolean mShouldDisallowCall;
139 private final boolean mShouldRejectCall;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800140 private final boolean mShouldSilenceCall;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800141 private final boolean mShouldSkipCallLog;
142 private final boolean mShouldSkipNotification;
143
144 private CallResponse(
145 boolean shouldDisallowCall,
146 boolean shouldRejectCall,
Usman Abdullah47b392d2019-03-06 15:54:56 -0800147 boolean shouldSilenceCall,
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800148 boolean shouldSkipCallLog,
149 boolean shouldSkipNotification) {
150 if (!shouldDisallowCall
151 && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
152 throw new IllegalStateException("Invalid response state for allowed call.");
153 }
154
155 mShouldDisallowCall = shouldDisallowCall;
156 mShouldRejectCall = shouldRejectCall;
157 mShouldSkipCallLog = shouldSkipCallLog;
158 mShouldSkipNotification = shouldSkipNotification;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800159 mShouldSilenceCall = shouldSilenceCall;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800160 }
161
162 /*
163 * @return Whether the incoming call should be blocked.
164 */
165 public boolean getDisallowCall() {
166 return mShouldDisallowCall;
167 }
168
169 /*
170 * @return Whether the incoming call should be disconnected as if the user had manually
171 * rejected it.
172 */
173 public boolean getRejectCall() {
174 return mShouldRejectCall;
175 }
176
177 /*
Usman Abdullah47b392d2019-03-06 15:54:56 -0800178 * @return Whether the ringtone should be silenced for the incoming call.
179 */
180 public boolean getSilenceCall() {
181 return mShouldSilenceCall;
182 }
183
184 /*
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800185 * @return Whether the incoming call should not be displayed in the call log.
186 */
187 public boolean getSkipCallLog() {
188 return mShouldSkipCallLog;
189 }
190
191 /*
192 * @return Whether a missed call notification should not be shown for the incoming call.
193 */
194 public boolean getSkipNotification() {
195 return mShouldSkipNotification;
196 }
197
Sailesh Nepalf4460712016-01-27 16:45:51 -0800198 public static class Builder {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800199 private boolean mShouldDisallowCall;
200 private boolean mShouldRejectCall;
Usman Abdullah47b392d2019-03-06 15:54:56 -0800201 private boolean mShouldSilenceCall;
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800202 private boolean mShouldSkipCallLog;
203 private boolean mShouldSkipNotification;
204
Tyler Gunne0caec72018-11-30 14:21:18 -0800205 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800206 * Sets whether the incoming call should be blocked.
207 */
208 public Builder setDisallowCall(boolean shouldDisallowCall) {
209 mShouldDisallowCall = shouldDisallowCall;
210 return this;
211 }
212
Tyler Gunne0caec72018-11-30 14:21:18 -0800213 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800214 * Sets whether the incoming call should be disconnected as if the user had manually
215 * rejected it. This property should only be set to true if the call is disallowed.
216 */
217 public Builder setRejectCall(boolean shouldRejectCall) {
218 mShouldRejectCall = shouldRejectCall;
219 return this;
220 }
221
Tyler Gunne0caec72018-11-30 14:21:18 -0800222 /**
Usman Abdullah47b392d2019-03-06 15:54:56 -0800223 * Sets whether ringing should be silenced for the incoming call. When set
224 * to {@code true}, the Telecom framework will not play a ringtone for the call.
225 * The call will, however, still be sent to the default dialer app if it is not blocked.
226 * A {@link CallScreeningService} can use this to ensure a potential nuisance call is
227 * still surfaced to the user, but in a less intrusive manner.
228 *
229 * Setting this to true only makes sense when the call has not been disallowed
230 * using {@link #setDisallowCall(boolean)}.
231 */
232 public @NonNull Builder setSilenceCall(boolean shouldSilenceCall) {
233 mShouldSilenceCall = shouldSilenceCall;
234 return this;
235 }
236
237 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800238 * Sets whether the incoming call should not be displayed in the call log. This property
239 * should only be set to true if the call is disallowed.
Tyler Gunne0caec72018-11-30 14:21:18 -0800240 * <p>
241 * Note: Calls will still be logged with type
242 * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
243 * is set.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800244 */
245 public Builder setSkipCallLog(boolean shouldSkipCallLog) {
246 mShouldSkipCallLog = shouldSkipCallLog;
247 return this;
248 }
249
Tyler Gunne0caec72018-11-30 14:21:18 -0800250 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800251 * Sets whether a missed call notification should not be shown for the incoming call.
252 * This property should only be set to true if the call is disallowed.
253 */
254 public Builder setSkipNotification(boolean shouldSkipNotification) {
255 mShouldSkipNotification = shouldSkipNotification;
256 return this;
257 }
258
259 public CallResponse build() {
260 return new CallResponse(
261 mShouldDisallowCall,
262 mShouldRejectCall,
Usman Abdullah47b392d2019-03-06 15:54:56 -0800263 mShouldSilenceCall,
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800264 mShouldSkipCallLog,
265 mShouldSkipNotification);
266 }
267 }
268 }
269
270 public CallScreeningService() {
271 }
272
273 @Override
274 public IBinder onBind(Intent intent) {
275 Log.v(this, "onBind");
276 return new CallScreeningBinder();
277 }
278
279 @Override
280 public boolean onUnbind(Intent intent) {
281 Log.v(this, "onUnbind");
282 return false;
283 }
284
285 /**
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800286 * Called when a new incoming or outgoing call is added which is not in the user's contact list.
287 * <p>
288 * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by
289 * calling
290 * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}.
291 * Your app can tell if a call is an incoming call by checking to see if
292 * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}.
293 * <p>
Tyler Gunne0caec72018-11-30 14:21:18 -0800294 * Note: The {@link Call.Details} instance provided to a call screening service will only have
295 * the following properties set. The rest of the {@link Call.Details} properties will be set to
296 * their default value or {@code null}.
297 * <ul>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800298 * <li>{@link Call.Details#getCallDirection()}</li>
Tyler Gunne0caec72018-11-30 14:21:18 -0800299 * <li>{@link Call.Details#getConnectTimeMillis()}</li>
300 * <li>{@link Call.Details#getCreationTimeMillis()}</li>
301 * <li>{@link Call.Details#getHandle()}</li>
302 * <li>{@link Call.Details#getHandlePresentation()}</li>
303 * </ul>
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800304 * <p>
305 * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme}
306 * is {@link PhoneAccount#SCHEME_TEL} are passed for call
307 * screening. Further, only calls which are not in the user's contacts are passed for
308 * screening. For outgoing calls, no post-dial digits are passed.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800309 *
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800310 * @param callDetails Information about a new call, see {@link Call.Details}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800311 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800312 public abstract void onScreenCall(@NonNull Call.Details callDetails);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800313
314 /**
Usman Abdullah47b392d2019-03-06 15:54:56 -0800315 * Responds to the given incoming call, either allowing it, silencing it or disallowing it.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800316 * <p>
317 * The {@link CallScreeningService} calls this method to inform the system whether the call
Usman Abdullah47b392d2019-03-06 15:54:56 -0800318 * should be silently blocked or not. In the event that it should not be blocked, it may
319 * also be requested to ring silently.
Tyler Gunn9e76fd19b2018-12-17 09:56:11 -0800320 * <p>
321 * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is
322 * {@link Call.Details#DIRECTION_INCOMING}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800323 *
324 * @param callDetails The call to allow.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800325 * <p>
326 * Must be the same {@link Call.Details call} which was provided to the
327 * {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800328 * @param response The {@link CallScreeningService.CallResponse} which contains information
329 * about how to respond to a call.
330 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800331 public final void respondToCall(@NonNull Call.Details callDetails,
332 @NonNull CallResponse response) {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800333 try {
334 if (response.getDisallowCall()) {
335 mCallScreeningAdapter.disallowCall(
336 callDetails.getTelecomCallId(),
337 response.getRejectCall(),
338 !response.getSkipCallLog(),
tonyzhu9e1d4f82018-10-22 15:11:31 +0800339 !response.getSkipNotification(),
340 new ComponentName(getPackageName(), getClass().getName()));
Usman Abdullah47b392d2019-03-06 15:54:56 -0800341 } else if (response.getSilenceCall()) {
342 mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId());
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800343 } else {
344 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
345 }
346 } catch (RemoteException e) {
347 }
348 }
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800349}