blob: 662874316a7f5ce9792e865563da9cb0b5897ede [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
19import android.annotation.SdkConstant;
20import android.app.Service;
tonyzhu9e1d4f82018-10-22 15:11:31 +080021import android.content.ComponentName;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080022import android.content.Intent;
23import android.os.Handler;
24import android.os.IBinder;
25import android.os.Looper;
26import android.os.Message;
27import android.os.RemoteException;
28
29import com.android.internal.os.SomeArgs;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080030import com.android.internal.telecom.ICallScreeningAdapter;
Tyler Gunne0caec72018-11-30 14:21:18 -080031import com.android.internal.telecom.ICallScreeningService;
Sailesh Nepal1bef3392016-01-24 18:21:53 -080032
33/**
34 * This service can be implemented by the default dialer (see
35 * {@link TelecomManager#getDefaultDialerPackage()}) to allow or disallow incoming calls before
36 * they are shown to a user.
37 * <p>
38 * Below is an example manifest registration for a {@code CallScreeningService}.
39 * <pre>
40 * {@code
41 * <service android:name="your.package.YourCallScreeningServiceImplementation"
42 * android:permission="android.permission.BIND_SCREENING_SERVICE">
43 * <intent-filter>
44 * <action android:name="android.telecom.CallScreeningService"/>
45 * </intent-filter>
46 * </service>
47 * }
48 * </pre>
49 */
50public abstract class CallScreeningService extends Service {
51 /**
52 * The {@link Intent} that must be declared as handled by the service.
53 */
54 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
55 public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
56
57 private static final int MSG_SCREEN_CALL = 1;
58
59 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
60 @Override
61 public void handleMessage(Message msg) {
62 switch (msg.what) {
63 case MSG_SCREEN_CALL:
64 SomeArgs args = (SomeArgs) msg.obj;
65 try {
66 mCallScreeningAdapter = (ICallScreeningAdapter) args.arg1;
67 onScreenCall(
68 Call.Details.createFromParcelableCall((ParcelableCall) args.arg2));
69 } finally {
70 args.recycle();
71 }
72 break;
73 }
74 }
75 };
76
77 private final class CallScreeningBinder extends ICallScreeningService.Stub {
78 @Override
79 public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) {
80 Log.v(this, "screenCall");
81 SomeArgs args = SomeArgs.obtain();
82 args.arg1 = adapter;
83 args.arg2 = call;
84 mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget();
85 }
86 }
87
88 private ICallScreeningAdapter mCallScreeningAdapter;
89
90 /*
91 * Information about how to respond to an incoming call.
92 */
Sailesh Nepalf4460712016-01-27 16:45:51 -080093 public static class CallResponse {
Sailesh Nepal1bef3392016-01-24 18:21:53 -080094 private final boolean mShouldDisallowCall;
95 private final boolean mShouldRejectCall;
96 private final boolean mShouldSkipCallLog;
97 private final boolean mShouldSkipNotification;
98
99 private CallResponse(
100 boolean shouldDisallowCall,
101 boolean shouldRejectCall,
102 boolean shouldSkipCallLog,
103 boolean shouldSkipNotification) {
104 if (!shouldDisallowCall
105 && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
106 throw new IllegalStateException("Invalid response state for allowed call.");
107 }
108
109 mShouldDisallowCall = shouldDisallowCall;
110 mShouldRejectCall = shouldRejectCall;
111 mShouldSkipCallLog = shouldSkipCallLog;
112 mShouldSkipNotification = shouldSkipNotification;
113 }
114
115 /*
116 * @return Whether the incoming call should be blocked.
117 */
118 public boolean getDisallowCall() {
119 return mShouldDisallowCall;
120 }
121
122 /*
123 * @return Whether the incoming call should be disconnected as if the user had manually
124 * rejected it.
125 */
126 public boolean getRejectCall() {
127 return mShouldRejectCall;
128 }
129
130 /*
131 * @return Whether the incoming call should not be displayed in the call log.
132 */
133 public boolean getSkipCallLog() {
134 return mShouldSkipCallLog;
135 }
136
137 /*
138 * @return Whether a missed call notification should not be shown for the incoming call.
139 */
140 public boolean getSkipNotification() {
141 return mShouldSkipNotification;
142 }
143
Sailesh Nepalf4460712016-01-27 16:45:51 -0800144 public static class Builder {
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800145 private boolean mShouldDisallowCall;
146 private boolean mShouldRejectCall;
147 private boolean mShouldSkipCallLog;
148 private boolean mShouldSkipNotification;
149
Tyler Gunne0caec72018-11-30 14:21:18 -0800150 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800151 * Sets whether the incoming call should be blocked.
152 */
153 public Builder setDisallowCall(boolean shouldDisallowCall) {
154 mShouldDisallowCall = shouldDisallowCall;
155 return this;
156 }
157
Tyler Gunne0caec72018-11-30 14:21:18 -0800158 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800159 * Sets whether the incoming call should be disconnected as if the user had manually
160 * rejected it. This property should only be set to true if the call is disallowed.
161 */
162 public Builder setRejectCall(boolean shouldRejectCall) {
163 mShouldRejectCall = shouldRejectCall;
164 return this;
165 }
166
Tyler Gunne0caec72018-11-30 14:21:18 -0800167 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800168 * Sets whether the incoming call should not be displayed in the call log. This property
169 * should only be set to true if the call is disallowed.
Tyler Gunne0caec72018-11-30 14:21:18 -0800170 * <p>
171 * Note: Calls will still be logged with type
172 * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
173 * is set.
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800174 */
175 public Builder setSkipCallLog(boolean shouldSkipCallLog) {
176 mShouldSkipCallLog = shouldSkipCallLog;
177 return this;
178 }
179
Tyler Gunne0caec72018-11-30 14:21:18 -0800180 /**
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800181 * Sets whether a missed call notification should not be shown for the incoming call.
182 * This property should only be set to true if the call is disallowed.
183 */
184 public Builder setSkipNotification(boolean shouldSkipNotification) {
185 mShouldSkipNotification = shouldSkipNotification;
186 return this;
187 }
188
189 public CallResponse build() {
190 return new CallResponse(
191 mShouldDisallowCall,
192 mShouldRejectCall,
193 mShouldSkipCallLog,
194 mShouldSkipNotification);
195 }
196 }
197 }
198
199 public CallScreeningService() {
200 }
201
202 @Override
203 public IBinder onBind(Intent intent) {
204 Log.v(this, "onBind");
205 return new CallScreeningBinder();
206 }
207
208 @Override
209 public boolean onUnbind(Intent intent) {
210 Log.v(this, "onUnbind");
211 return false;
212 }
213
214 /**
215 * Called when a new incoming call is added.
216 * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}
217 * should be called to allow or disallow the call.
Tyler Gunne0caec72018-11-30 14:21:18 -0800218 * <p>
219 * Note: The {@link Call.Details} instance provided to a call screening service will only have
220 * the following properties set. The rest of the {@link Call.Details} properties will be set to
221 * their default value or {@code null}.
222 * <ul>
223 * <li>{@link Call.Details#getState()}</li>
224 * <li>{@link Call.Details#getConnectTimeMillis()}</li>
225 * <li>{@link Call.Details#getCreationTimeMillis()}</li>
226 * <li>{@link Call.Details#getHandle()}</li>
227 * <li>{@link Call.Details#getHandlePresentation()}</li>
228 * </ul>
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800229 *
230 * @param callDetails Information about a new incoming call, see {@link Call.Details}.
231 */
232 public abstract void onScreenCall(Call.Details callDetails);
233
234 /**
235 * Responds to the given call, either allowing it or disallowing it.
236 *
237 * @param callDetails The call to allow.
238 * @param response The {@link CallScreeningService.CallResponse} which contains information
239 * about how to respond to a call.
240 */
241 public final void respondToCall(Call.Details callDetails, CallResponse response) {
242 try {
243 if (response.getDisallowCall()) {
244 mCallScreeningAdapter.disallowCall(
245 callDetails.getTelecomCallId(),
246 response.getRejectCall(),
247 !response.getSkipCallLog(),
tonyzhu9e1d4f82018-10-22 15:11:31 +0800248 !response.getSkipNotification(),
249 new ComponentName(getPackageName(), getClass().getName()));
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800250 } else {
251 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
252 }
253 } catch (RemoteException e) {
254 }
255 }
256}