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.SdkConstant;
     20 import android.app.Service;
     21 import android.content.Intent;
     22 import android.os.Handler;
     23 import android.os.IBinder;
     24 import android.os.Looper;
     25 import android.os.Message;
     26 import android.os.RemoteException;
     27 
     28 import com.android.internal.os.SomeArgs;
     29 import com.android.internal.telecom.ICallScreeningService;
     30 import 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  */
     49 public 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      */
     92     public static class CallResponse {
     93         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 
    143         public static class Builder {
    144             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 }
    240