• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.telecom;
18 
19 import android.annotation.NonNull;
20 import android.annotation.SdkConstant;
21 import android.annotation.SystemApi;
22 import android.annotation.TestApi;
23 import android.app.Service;
24 import android.content.ComponentName;
25 import android.content.Intent;
26 import android.content.pm.ServiceInfo;
27 import android.net.Uri;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.Looper;
31 import android.os.Message;
32 import android.os.RemoteException;
33 
34 import com.android.internal.os.SomeArgs;
35 import com.android.internal.telecom.ICallScreeningAdapter;
36 import com.android.internal.telecom.ICallScreeningService;
37 
38 /**
39  * This service can be implemented by the default dialer (see
40  * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
41  * incoming calls before they are shown to a user. A {@link CallScreeningService} can also see
42  * outgoing calls for the purpose of providing caller ID services for those calls.
43  * <p>
44  * Below is an example manifest registration for a {@code CallScreeningService}.
45  * <pre>
46  * {@code
47  * <service android:name="your.package.YourCallScreeningServiceImplementation"
48  *          android:permission="android.permission.BIND_SCREENING_SERVICE">
49  *      <intent-filter>
50  *          <action android:name="android.telecom.CallScreeningService"/>
51  *      </intent-filter>
52  * </service>
53  * }
54  * </pre>
55  * <p>
56  * A CallScreeningService performs two functions:
57  * <ol>
58  *     <li>Call blocking/screening - the service can choose which calls will ring on the user's
59  *     device, and which will be silently sent to voicemail.</li>
60  *     <li>Call identification - services which provide call identification functionality can
61  *     display a user-interface of their choosing which contains identifying information for a call.
62  *     </li>
63  * </ol>
64  * <p>
65  * <h2>Becoming the {@link CallScreeningService}</h2>
66  * Telecom will bind to a single app chosen by the user which implements the
67  * {@link CallScreeningService} API when there are new incoming and outgoing calls.
68  * <p>
69  * The code snippet below illustrates how your app can request that it fills the call screening
70  * role.
71  * <pre>
72  * {@code
73  * private static final int REQUEST_ID = 1;
74  *
75  * public void requestRole() {
76  *     RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
77  *     Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING);
78  *     startActivityForResult(intent, REQUEST_ID);
79  * }
80  *
81  * &#64;Override
82  * public void onActivityResult(int requestCode, int resultCode, Intent data) {
83  *     if (requestCode == REQUEST_ID) {
84  *         if (resultCode == android.app.Activity.RESULT_OK) {
85  *             // Your app is now the call screening app
86  *         } else {
87  *             // Your app is not the call screening app
88  *         }
89  *     }
90  * }
91  * </pre>
92  */
93 public abstract class CallScreeningService extends Service {
94     /**
95      * The {@link Intent} that must be declared as handled by the service.
96      */
97     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
98     public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
99 
100     private static final int MSG_SCREEN_CALL = 1;
101 
102     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
103         @Override
104         public void handleMessage(Message msg) {
105             switch (msg.what) {
106                 case MSG_SCREEN_CALL:
107                     SomeArgs args = (SomeArgs) msg.obj;
108                     try {
109                         mCallScreeningAdapter = (ICallScreeningAdapter) args.arg1;
110                         Call.Details callDetails = Call.Details
111                                 .createFromParcelableCall((ParcelableCall) args.arg2);
112                         onScreenCall(callDetails);
113                         if (callDetails.getCallDirection() == Call.Details.DIRECTION_OUTGOING) {
114                             mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
115                         }
116                     } catch (RemoteException e) {
117                         Log.w(this, "Exception when screening call: " + e);
118                     } finally {
119                         args.recycle();
120                     }
121                     break;
122             }
123         }
124     };
125 
126     private final class CallScreeningBinder extends ICallScreeningService.Stub {
127         @Override
screenCall(ICallScreeningAdapter adapter, ParcelableCall call)128         public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) {
129             Log.v(this, "screenCall");
130             SomeArgs args = SomeArgs.obtain();
131             args.arg1 = adapter;
132             args.arg2 = call;
133             mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget();
134         }
135     }
136 
137     private ICallScreeningAdapter mCallScreeningAdapter;
138 
139     /*
140      * Information about how to respond to an incoming call.
141      */
142     public static class CallResponse {
143         private final boolean mShouldDisallowCall;
144         private final boolean mShouldRejectCall;
145         private final boolean mShouldSilenceCall;
146         private final boolean mShouldSkipCallLog;
147         private final boolean mShouldSkipNotification;
148         private final boolean mShouldScreenCallViaAudioProcessing;
149 
CallResponse( boolean shouldDisallowCall, boolean shouldRejectCall, boolean shouldSilenceCall, boolean shouldSkipCallLog, boolean shouldSkipNotification, boolean shouldScreenCallViaAudioProcessing)150         private CallResponse(
151                 boolean shouldDisallowCall,
152                 boolean shouldRejectCall,
153                 boolean shouldSilenceCall,
154                 boolean shouldSkipCallLog,
155                 boolean shouldSkipNotification,
156                 boolean shouldScreenCallViaAudioProcessing) {
157             if (!shouldDisallowCall
158                     && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
159                 throw new IllegalStateException("Invalid response state for allowed call.");
160             }
161 
162             if (shouldDisallowCall && shouldScreenCallViaAudioProcessing) {
163                 throw new IllegalStateException("Invalid response state for allowed call.");
164             }
165 
166             mShouldDisallowCall = shouldDisallowCall;
167             mShouldRejectCall = shouldRejectCall;
168             mShouldSkipCallLog = shouldSkipCallLog;
169             mShouldSkipNotification = shouldSkipNotification;
170             mShouldSilenceCall = shouldSilenceCall;
171             mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
172         }
173 
174         /*
175          * @return Whether the incoming call should be blocked.
176          */
getDisallowCall()177         public boolean getDisallowCall() {
178             return mShouldDisallowCall;
179         }
180 
181         /*
182          * @return Whether the incoming call should be disconnected as if the user had manually
183          * rejected it.
184          */
getRejectCall()185         public boolean getRejectCall() {
186             return mShouldRejectCall;
187         }
188 
189         /*
190          * @return Whether the ringtone should be silenced for the incoming call.
191          */
getSilenceCall()192         public boolean getSilenceCall() {
193             return mShouldSilenceCall;
194         }
195 
196         /*
197          * @return Whether the incoming call should not be displayed in the call log.
198          */
getSkipCallLog()199         public boolean getSkipCallLog() {
200             return mShouldSkipCallLog;
201         }
202 
203         /*
204          * @return Whether a missed call notification should not be shown for the incoming call.
205          */
getSkipNotification()206         public boolean getSkipNotification() {
207             return mShouldSkipNotification;
208         }
209 
210         /**
211          * @return Whether we should enter the {@link Call#STATE_AUDIO_PROCESSING} state to allow
212          * for further screening of the call.
213          * @hide
214          */
getShouldScreenCallViaAudioProcessing()215         public boolean getShouldScreenCallViaAudioProcessing() {
216             return mShouldScreenCallViaAudioProcessing;
217         }
218 
219         public static class Builder {
220             private boolean mShouldDisallowCall;
221             private boolean mShouldRejectCall;
222             private boolean mShouldSilenceCall;
223             private boolean mShouldSkipCallLog;
224             private boolean mShouldSkipNotification;
225             private boolean mShouldScreenCallViaAudioProcessing;
226 
227             /**
228              * Sets whether the incoming call should be blocked.
229              */
setDisallowCall(boolean shouldDisallowCall)230             public Builder setDisallowCall(boolean shouldDisallowCall) {
231                 mShouldDisallowCall = shouldDisallowCall;
232                 return this;
233             }
234 
235             /**
236              * Sets whether the incoming call should be disconnected as if the user had manually
237              * rejected it. This property should only be set to true if the call is disallowed.
238              */
setRejectCall(boolean shouldRejectCall)239             public Builder setRejectCall(boolean shouldRejectCall) {
240                 mShouldRejectCall = shouldRejectCall;
241                 return this;
242             }
243 
244             /**
245              * Sets whether ringing should be silenced for the incoming call.  When set
246              * to {@code true}, the Telecom framework will not play a ringtone for the call.
247              * The call will, however, still be sent to the default dialer app if it is not blocked.
248              * A {@link CallScreeningService} can use this to ensure a potential nuisance call is
249              * still surfaced to the user, but in a less intrusive manner.
250              *
251              * Setting this to true only makes sense when the call has not been disallowed
252              * using {@link #setDisallowCall(boolean)}.
253              */
setSilenceCall(boolean shouldSilenceCall)254             public @NonNull Builder setSilenceCall(boolean shouldSilenceCall) {
255                 mShouldSilenceCall = shouldSilenceCall;
256                 return this;
257             }
258 
259             /**
260              * Sets whether the incoming call should not be displayed in the call log. This property
261              * should only be set to true if the call is disallowed.
262              * <p>
263              * Note: Calls will still be logged with type
264              * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
265              * is set.
266              */
setSkipCallLog(boolean shouldSkipCallLog)267             public Builder setSkipCallLog(boolean shouldSkipCallLog) {
268                 mShouldSkipCallLog = shouldSkipCallLog;
269                 return this;
270             }
271 
272             /**
273              * Sets whether a missed call notification should not be shown for the incoming call.
274              * This property should only be set to true if the call is disallowed.
275              */
setSkipNotification(boolean shouldSkipNotification)276             public Builder setSkipNotification(boolean shouldSkipNotification) {
277                 mShouldSkipNotification = shouldSkipNotification;
278                 return this;
279             }
280 
281             /**
282              * Sets whether to request background audio processing so that the in-call service can
283              * screen the call further. If set to {@code true}, {@link #setDisallowCall} should be
284              * called with {@code false}, and all other parameters in this builder will be ignored.
285              * <p>
286              * This request will only be honored if the {@link CallScreeningService} shares the same
287              * uid as the default dialer app. Otherwise, the call will go through as usual.
288              * <p>
289              * Apps built with SDK version {@link android.os.Build.VERSION_CODES#R} or later which
290              * are using the microphone as part of audio processing should specify the
291              * foreground service type using the attribute
292              * {@link android.R.attr#foregroundServiceType} in the {@link CallScreeningService}
293              * service element of the app's manifest file.
294              * The {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE} attribute should be
295              * specified.
296              * @see
297              * <a href="https://developer.android.com/preview/privacy/foreground-service-types">
298              *     the Android Developer Site</a> for more information.
299              *
300              * @param shouldScreenCallViaAudioProcessing Whether to request further call screening.
301              * @hide
302              */
303             @SystemApi
304             @TestApi
setShouldScreenCallViaAudioProcessing( boolean shouldScreenCallViaAudioProcessing)305             public @NonNull Builder setShouldScreenCallViaAudioProcessing(
306                     boolean shouldScreenCallViaAudioProcessing) {
307                 mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
308                 return this;
309             }
310 
build()311             public CallResponse build() {
312                 return new CallResponse(
313                         mShouldDisallowCall,
314                         mShouldRejectCall,
315                         mShouldSilenceCall,
316                         mShouldSkipCallLog,
317                         mShouldSkipNotification,
318                         mShouldScreenCallViaAudioProcessing);
319             }
320        }
321     }
322 
CallScreeningService()323     public CallScreeningService() {
324     }
325 
326     @Override
onBind(Intent intent)327     public IBinder onBind(Intent intent) {
328         Log.v(this, "onBind");
329         return new CallScreeningBinder();
330     }
331 
332     @Override
onUnbind(Intent intent)333     public boolean onUnbind(Intent intent) {
334         Log.v(this, "onUnbind");
335         return false;
336     }
337 
338     /**
339      * Called when a new incoming or outgoing call is added which is not in the user's contact list.
340      * <p>
341      * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by
342      * calling
343      * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}.
344      * Your app can tell if a call is an incoming call by checking to see if
345      * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}.
346      * <p>
347      * Note: The {@link Call.Details} instance provided to a call screening service will only have
348      * the following properties set.  The rest of the {@link Call.Details} properties will be set to
349      * their default value or {@code null}.
350      * <ul>
351      *     <li>{@link Call.Details#getCallDirection()}</li>
352      *     <li>{@link Call.Details#getConnectTimeMillis()}</li>
353      *     <li>{@link Call.Details#getCreationTimeMillis()}</li>
354      *     <li>{@link Call.Details#getHandle()}</li>
355      *     <li>{@link Call.Details#getHandlePresentation()}</li>
356      * </ul>
357      * <p>
358      * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme}
359      * is {@link PhoneAccount#SCHEME_TEL} are passed for call
360      * screening.  Further, only calls which are not in the user's contacts are passed for
361      * screening.  For outgoing calls, no post-dial digits are passed.
362      *
363      * @param callDetails Information about a new call, see {@link Call.Details}.
364      */
onScreenCall(@onNull Call.Details callDetails)365     public abstract void onScreenCall(@NonNull Call.Details callDetails);
366 
367     /**
368      * Responds to the given incoming call, either allowing it, silencing it or disallowing it.
369      * <p>
370      * The {@link CallScreeningService} calls this method to inform the system whether the call
371      * should be silently blocked or not. In the event that it should not be blocked, it may
372      * also be requested to ring silently.
373      * <p>
374      * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is
375      * {@link Call.Details#DIRECTION_INCOMING}.
376      *
377      * @param callDetails The call to allow.
378      *                    <p>
379      *                    Must be the same {@link Call.Details call} which was provided to the
380      *                    {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
381      * @param response The {@link CallScreeningService.CallResponse} which contains information
382      * about how to respond to a call.
383      */
respondToCall(@onNull Call.Details callDetails, @NonNull CallResponse response)384     public final void respondToCall(@NonNull Call.Details callDetails,
385             @NonNull CallResponse response) {
386         try {
387             if (response.getDisallowCall()) {
388                 mCallScreeningAdapter.disallowCall(
389                         callDetails.getTelecomCallId(),
390                         response.getRejectCall(),
391                         !response.getSkipCallLog(),
392                         !response.getSkipNotification(),
393                         new ComponentName(getPackageName(), getClass().getName()));
394             } else if (response.getSilenceCall()) {
395                 mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId());
396             } else if (response.getShouldScreenCallViaAudioProcessing()) {
397                 mCallScreeningAdapter.screenCallFurther(callDetails.getTelecomCallId());
398             } else {
399                 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
400             }
401         } catch (RemoteException e) {
402         }
403     }
404 }
405