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