blob: be96b3cac6f6698ba7e2c8369ba8dc6015eb2449 [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;
21import android.app.Service;
tonyzhu9e1d4f82018-10-22 15:11:31 +080022import android.content.ComponentName;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080023import android.content.Intent;
24import android.os.Handler;
25import android.os.IBinder;
26import android.os.Looper;
27import android.os.Message;
28import android.os.RemoteException;
29
30import com.android.internal.os.SomeArgs;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080031import com.android.internal.telecom.ICallScreeningAdapter;
Tyler Gunne0caec72018-11-30 14:21:18 -080032import com.android.internal.telecom.ICallScreeningService;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080033
34/**
35 * This service can be implemented by the default dialer (see
36 * {@link TelecomManager#getDefaultDialerPackage()}) to allow or disallow incoming calls before
37 * they are shown to a user.
38 * <p>
39 * Below is an example manifest registration for a {@code CallScreeningService}.
40 * <pre>
41 * {@code
42 * <service android:name="your.package.YourCallScreeningServiceImplementation"
43 * android:permission="android.permission.BIND_SCREENING_SERVICE">
44 * <intent-filter>
45 * <action android:name="android.telecom.CallScreeningService"/>
46 * </intent-filter>
47 * </service>
48 * }
49 * </pre>
Tyler Gunn7e45b722018-12-04 12:56:45 -080050 * <p>
51 * A CallScreeningService performs two functions:
52 * <ol>
53 * <li>Call blocking/screening - the service can choose which calls will ring on the user's
54 * device, and which will be silently sent to voicemail.</li>
55 * <li>Call identification - the service can optionally provide {@link CallIdentification}
56 * information about a {@link Call.Details call} which will be shown to the user in the
57 * Dialer app.</li>
58 * </ol>
Sailesh Nepal1bef3392016-01-24 18:21:53 -080059 */
60public abstract class CallScreeningService extends Service {
61 /**
62 * The {@link Intent} that must be declared as handled by the service.
63 */
64 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
65 public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
66
67 private static final int MSG_SCREEN_CALL = 1;
68
69 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
70 @Override
71 public void handleMessage(Message msg) {
72 switch (msg.what) {
73 case MSG_SCREEN_CALL:
74 SomeArgs args = (SomeArgs) msg.obj;
75 try {
76 mCallScreeningAdapter = (ICallScreeningAdapter) args.arg1;
77 onScreenCall(
78 Call.Details.createFromParcelableCall((ParcelableCall) args.arg2));
79 } finally {
80 args.recycle();
81 }
82 break;
83 }
84 }
85 };
86
87 private final class CallScreeningBinder extends ICallScreeningService.Stub {
88 @Override
89 public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) {
90 Log.v(this, "screenCall");
91 SomeArgs args = SomeArgs.obtain();
92 args.arg1 = adapter;
93 args.arg2 = call;
94 mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget();
95 }
96 }
97
98 private ICallScreeningAdapter mCallScreeningAdapter;
99
100 /*
101 * Information about how to respond to an incoming call.
102 */
Sailesh Nepalf4460712016-01-27 16:45:51 -0800103 public static class CallResponse {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800104 private final boolean mShouldDisallowCall;
105 private final boolean mShouldRejectCall;
106 private final boolean mShouldSkipCallLog;
107 private final boolean mShouldSkipNotification;
108
109 private CallResponse(
110 boolean shouldDisallowCall,
111 boolean shouldRejectCall,
112 boolean shouldSkipCallLog,
113 boolean shouldSkipNotification) {
114 if (!shouldDisallowCall
115 && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
116 throw new IllegalStateException("Invalid response state for allowed call.");
117 }
118
119 mShouldDisallowCall = shouldDisallowCall;
120 mShouldRejectCall = shouldRejectCall;
121 mShouldSkipCallLog = shouldSkipCallLog;
122 mShouldSkipNotification = shouldSkipNotification;
123 }
124
125 /*
126 * @return Whether the incoming call should be blocked.
127 */
128 public boolean getDisallowCall() {
129 return mShouldDisallowCall;
130 }
131
132 /*
133 * @return Whether the incoming call should be disconnected as if the user had manually
134 * rejected it.
135 */
136 public boolean getRejectCall() {
137 return mShouldRejectCall;
138 }
139
140 /*
141 * @return Whether the incoming call should not be displayed in the call log.
142 */
143 public boolean getSkipCallLog() {
144 return mShouldSkipCallLog;
145 }
146
147 /*
148 * @return Whether a missed call notification should not be shown for the incoming call.
149 */
150 public boolean getSkipNotification() {
151 return mShouldSkipNotification;
152 }
153
Sailesh Nepalf4460712016-01-27 16:45:51 -0800154 public static class Builder {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800155 private boolean mShouldDisallowCall;
156 private boolean mShouldRejectCall;
157 private boolean mShouldSkipCallLog;
158 private boolean mShouldSkipNotification;
159
Tyler Gunne0caec72018-11-30 14:21:18 -0800160 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800161 * Sets whether the incoming call should be blocked.
162 */
163 public Builder setDisallowCall(boolean shouldDisallowCall) {
164 mShouldDisallowCall = shouldDisallowCall;
165 return this;
166 }
167
Tyler Gunne0caec72018-11-30 14:21:18 -0800168 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800169 * Sets whether the incoming call should be disconnected as if the user had manually
170 * rejected it. This property should only be set to true if the call is disallowed.
171 */
172 public Builder setRejectCall(boolean shouldRejectCall) {
173 mShouldRejectCall = shouldRejectCall;
174 return this;
175 }
176
Tyler Gunne0caec72018-11-30 14:21:18 -0800177 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800178 * Sets whether the incoming call should not be displayed in the call log. This property
179 * should only be set to true if the call is disallowed.
Tyler Gunne0caec72018-11-30 14:21:18 -0800180 * <p>
181 * Note: Calls will still be logged with type
182 * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
183 * is set.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800184 */
185 public Builder setSkipCallLog(boolean shouldSkipCallLog) {
186 mShouldSkipCallLog = shouldSkipCallLog;
187 return this;
188 }
189
Tyler Gunne0caec72018-11-30 14:21:18 -0800190 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800191 * Sets whether a missed call notification should not be shown for the incoming call.
192 * This property should only be set to true if the call is disallowed.
193 */
194 public Builder setSkipNotification(boolean shouldSkipNotification) {
195 mShouldSkipNotification = shouldSkipNotification;
196 return this;
197 }
198
199 public CallResponse build() {
200 return new CallResponse(
201 mShouldDisallowCall,
202 mShouldRejectCall,
203 mShouldSkipCallLog,
204 mShouldSkipNotification);
205 }
206 }
207 }
208
209 public CallScreeningService() {
210 }
211
212 @Override
213 public IBinder onBind(Intent intent) {
214 Log.v(this, "onBind");
215 return new CallScreeningBinder();
216 }
217
218 @Override
219 public boolean onUnbind(Intent intent) {
220 Log.v(this, "onUnbind");
221 return false;
222 }
223
224 /**
225 * Called when a new incoming call is added.
226 * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}
227 * should be called to allow or disallow the call.
Tyler Gunne0caec72018-11-30 14:21:18 -0800228 * <p>
229 * Note: The {@link Call.Details} instance provided to a call screening service will only have
230 * the following properties set. The rest of the {@link Call.Details} properties will be set to
231 * their default value or {@code null}.
232 * <ul>
233 * <li>{@link Call.Details#getState()}</li>
234 * <li>{@link Call.Details#getConnectTimeMillis()}</li>
235 * <li>{@link Call.Details#getCreationTimeMillis()}</li>
236 * <li>{@link Call.Details#getHandle()}</li>
237 * <li>{@link Call.Details#getHandlePresentation()}</li>
238 * </ul>
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800239 *
240 * @param callDetails Information about a new incoming call, see {@link Call.Details}.
241 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800242 public abstract void onScreenCall(@NonNull Call.Details callDetails);
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800243
244 /**
245 * Responds to the given call, either allowing it or disallowing it.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800246 * <p>
247 * The {@link CallScreeningService} calls this method to inform the system whether the call
248 * should be silently blocked or not.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800249 *
250 * @param callDetails The call to allow.
Tyler Gunn7e45b722018-12-04 12:56:45 -0800251 * <p>
252 * Must be the same {@link Call.Details call} which was provided to the
253 * {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800254 * @param response The {@link CallScreeningService.CallResponse} which contains information
255 * about how to respond to a call.
256 */
Tyler Gunn7e45b722018-12-04 12:56:45 -0800257 public final void respondToCall(@NonNull Call.Details callDetails,
258 @NonNull CallResponse response) {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800259 try {
260 if (response.getDisallowCall()) {
261 mCallScreeningAdapter.disallowCall(
262 callDetails.getTelecomCallId(),
263 response.getRejectCall(),
264 !response.getSkipCallLog(),
tonyzhu9e1d4f82018-10-22 15:11:31 +0800265 !response.getSkipNotification(),
266 new ComponentName(getPackageName(), getClass().getName()));
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800267 } else {
268 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
269 }
270 } catch (RemoteException e) {
271 }
272 }
Tyler Gunn7e45b722018-12-04 12:56:45 -0800273
274 /**
275 * Provide {@link CallIdentification} information about a {@link Call.Details call}.
276 * <p>
277 * The {@link CallScreeningService} calls this method to provide information it has identified
278 * about a {@link Call.Details call}. This information will potentially be shown to the user
279 * in the {@link InCallService dialer} app. It will be logged to the
280 * {@link android.provider.CallLog}.
281 * <p>
282 * A {@link CallScreeningService} should only call this method for calls for which it is able to
283 * provide some {@link CallIdentification} for. {@link CallIdentification} instances with no
284 * fields set will be ignored by the system.
285 *
286 * @param callDetails The call to provide information for.
287 * <p>
288 * Must be the same {@link Call.Details call} which was provided to the
289 * {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
290 * @param identification An instance of {@link CallIdentification} with information about the
291 * {@link Call.Details call}.
292 */
293 public final void provideCallIdentification(@NonNull Call.Details callDetails,
294 @NonNull CallIdentification identification) {
295 try {
296 mCallScreeningAdapter.provideCallIdentification(callDetails.getTelecomCallId(),
297 identification);
298 } catch (RemoteException e) {
299 }
300 }
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800301}