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