blob: 7db69407ad3d03ba8166c5fe3930fcc5b176cec9 [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;
30import com.android.internal.telecom.ICallScreeningService;
31import com.android.internal.telecom.ICallScreeningAdapter;
32
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
150 /*
151 * Sets whether the incoming call should be blocked.
152 */
153 public Builder setDisallowCall(boolean shouldDisallowCall) {
154 mShouldDisallowCall = shouldDisallowCall;
155 return this;
156 }
157
158 /*
159 * 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
167 /*
168 * 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.
170 */
171 public Builder setSkipCallLog(boolean shouldSkipCallLog) {
172 mShouldSkipCallLog = shouldSkipCallLog;
173 return this;
174 }
175
176 /*
177 * Sets whether a missed call notification should not be shown for the incoming call.
178 * This property should only be set to true if the call is disallowed.
179 */
180 public Builder setSkipNotification(boolean shouldSkipNotification) {
181 mShouldSkipNotification = shouldSkipNotification;
182 return this;
183 }
184
185 public CallResponse build() {
186 return new CallResponse(
187 mShouldDisallowCall,
188 mShouldRejectCall,
189 mShouldSkipCallLog,
190 mShouldSkipNotification);
191 }
192 }
193 }
194
195 public CallScreeningService() {
196 }
197
198 @Override
199 public IBinder onBind(Intent intent) {
200 Log.v(this, "onBind");
201 return new CallScreeningBinder();
202 }
203
204 @Override
205 public boolean onUnbind(Intent intent) {
206 Log.v(this, "onUnbind");
207 return false;
208 }
209
210 /**
211 * Called when a new incoming call is added.
212 * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}
213 * should be called to allow or disallow the call.
214 *
215 * @param callDetails Information about a new incoming call, see {@link Call.Details}.
216 */
217 public abstract void onScreenCall(Call.Details callDetails);
218
219 /**
220 * Responds to the given call, either allowing it or disallowing it.
221 *
222 * @param callDetails The call to allow.
223 * @param response The {@link CallScreeningService.CallResponse} which contains information
224 * about how to respond to a call.
225 */
226 public final void respondToCall(Call.Details callDetails, CallResponse response) {
227 try {
228 if (response.getDisallowCall()) {
229 mCallScreeningAdapter.disallowCall(
230 callDetails.getTelecomCallId(),
231 response.getRejectCall(),
232 !response.getSkipCallLog(),
tonyzhu9e1d4f82018-10-22 15:11:31 +0800233 !response.getSkipNotification(),
234 new ComponentName(getPackageName(), getClass().getName()));
Sailesh Nepal1bef3392016-01-24 18:21:53 -0800235 } else {
236 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
237 }
238 } catch (RemoteException e) {
239 }
240 }
241}