• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 com.android.server.telecom;
18 
19 import android.Manifest;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.ServiceConnection;
24 import android.content.pm.PackageManager;
25 import android.content.pm.ResolveInfo;
26 import android.os.Handler;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.os.UserHandle;
30 import android.telecom.CallScreeningService;
31 import android.telecom.Log;
32 import android.telecom.Logging.Session;
33 import android.text.TextUtils;
34 
35 import com.android.internal.telecom.ICallScreeningAdapter;
36 import com.android.internal.telecom.ICallScreeningService;
37 
38 import java.util.List;
39 import java.util.concurrent.CompletableFuture;
40 
41 /**
42  * Helper class for performing operations with {@link CallScreeningService}s.
43  */
44 public class CallScreeningServiceHelper {
45     private static final String TAG = CallScreeningServiceHelper.class.getSimpleName();
46 
47     /**
48      * Abstracts away dependency on the {@link PackageManager} required to fetch the label for an
49      * app.
50      */
51     public interface AppLabelProxy {
getAppLabel(String packageName)52         CharSequence getAppLabel(String packageName);
53     }
54 
55     /**
56      * Implementation of {@link CallScreeningService} adapter AIDL; provides a means for responses
57      * from the call screening service to be handled.
58      */
59     private class CallScreeningAdapter extends ICallScreeningAdapter.Stub {
60         @Override
allowCall(String s)61         public void allowCall(String s) throws RemoteException {
62             // no-op; we don't allow this on outgoing calls.
63         }
64 
65         @Override
silenceCall(String s)66         public void silenceCall(String s) throws RemoteException {
67             // no-op; we don't allow this on outgoing calls.
68         }
69 
70         @Override
disallowCall(String s, boolean b, boolean b1, boolean b2, ComponentName componentName)71         public void disallowCall(String s, boolean b, boolean b1, boolean b2,
72                 ComponentName componentName) throws RemoteException {
73             // no-op; we don't allow this on outgoing calls.
74         }
75     }
76 
77     private final ParcelableCallUtils.Converter mParcelableCallUtilsConverter;
78     private final TelecomSystem.SyncRoot mTelecomLock;
79     private final Call mCall;
80     private final UserHandle mUserHandle;
81     private final Context mContext;
82     private final AppLabelProxy mAppLabelProxy;
83     private final Session mLoggingSession;
84     private CompletableFuture mFuture;
85     private String mPackageName;
86 
CallScreeningServiceHelper(Context context, TelecomSystem.SyncRoot telecomLock, String packageName, ParcelableCallUtils.Converter converter, UserHandle userHandle, Call call, AppLabelProxy appLabelProxy)87     public CallScreeningServiceHelper(Context context, TelecomSystem.SyncRoot telecomLock,
88             String packageName, ParcelableCallUtils.Converter converter,
89             UserHandle userHandle, Call call, AppLabelProxy appLabelProxy) {
90         mContext = context;
91         mTelecomLock = telecomLock;
92         mParcelableCallUtilsConverter = converter;
93         mCall = call;
94         mUserHandle = userHandle;
95         mPackageName = packageName;
96         mAppLabelProxy = appLabelProxy;
97         mLoggingSession = Log.createSubsession();
98     }
99 
100     /**
101      * Builds a {@link CompletableFuture} which performs a bind to a {@link CallScreeningService}
102      * @return
103      */
process()104     public CompletableFuture process() {
105         Log.d(this, "process");
106         return bindAndGetCallIdentification();
107     }
108 
bindAndGetCallIdentification()109     public CompletableFuture bindAndGetCallIdentification() {
110         Log.d(this, "bindAndGetCallIdentification");
111         if (mPackageName == null) {
112             return CompletableFuture.completedFuture(null);
113         }
114 
115         mFuture = new CompletableFuture();
116 
117         ServiceConnection serviceConnection = new ServiceConnection() {
118             @Override
119             public void onServiceConnected(ComponentName name, IBinder service) {
120                 ICallScreeningService screeningService =
121                         ICallScreeningService.Stub.asInterface(service);
122                 Log.continueSession(mLoggingSession, "CSSH.oSC");
123                 try {
124                     try {
125                         // Note: for outgoing calls, never include the restricted extras.
126                         screeningService.screenCall(new CallScreeningAdapter(),
127                                 mParcelableCallUtilsConverter.toParcelableCallForScreening(mCall,
128                                         false /* areRestrictedExtrasIncluded */));
129                     } catch (RemoteException e) {
130                         Log.w(CallScreeningServiceHelper.this,
131                                 "Cancelling call id due to remote exception");
132                         mFuture.complete(null);
133                     }
134                 } finally {
135                     Log.endSession();
136                 }
137             }
138 
139             @Override
140             public void onServiceDisconnected(ComponentName name) {
141                 // No locking needed -- CompletableFuture only lets one thread call complete.
142                 Log.continueSession(mLoggingSession, "CSSH.oSD");
143                 try {
144                     if (!mFuture.isDone()) {
145                         Log.w(CallScreeningServiceHelper.this,
146                                 "Cancelling outgoing call screen due to service disconnect.");
147                     }
148                     mFuture.complete(null);
149                 } finally {
150                     Log.endSession();
151                 }
152             }
153         };
154 
155         if (!bindCallScreeningService(mContext, mUserHandle, mPackageName, serviceConnection)) {
156             Log.i(this, "bindAndGetCallIdentification - bind failed");
157             Log.addEvent(mCall, LogUtils.Events.BIND_SCREENING, mPackageName);
158             mFuture.complete(null);
159         }
160 
161         // Set up a timeout so that we're not waiting forever for the caller ID information.
162         Handler handler = new Handler();
163         handler.postDelayed(() -> {
164                     // No locking needed -- CompletableFuture only lets one thread call complete.
165                     Log.continueSession(mLoggingSession, "CSSH.timeout");
166                     try {
167                         if (!mFuture.isDone()) {
168                             Log.w(TAG, "Cancelling call id process due to timeout");
169                         }
170                         mFuture.complete(null);
171                     } finally {
172                         Log.endSession();
173                     }
174                 },
175                 Timeouts.getCallScreeningTimeoutMillis(mContext.getContentResolver()));
176         return mFuture;
177     }
178 
179     /**
180      * Binds to a {@link CallScreeningService}.
181      * @param context The current context.
182      * @param userHandle User to bind as.
183      * @param packageName Package name of the {@link CallScreeningService}.
184      * @param serviceConnection The {@link ServiceConnection} to be notified of binding.
185      * @return {@code true} if binding succeeds, {@code false} otherwise.
186      */
bindCallScreeningService(Context context, UserHandle userHandle, String packageName, ServiceConnection serviceConnection)187     public static boolean bindCallScreeningService(Context context, UserHandle userHandle,
188             String packageName, ServiceConnection serviceConnection) {
189         if (TextUtils.isEmpty(packageName)) {
190             Log.i(TAG, "PackageName is empty. Not performing call screening.");
191             return false;
192         }
193 
194         Intent intent = new Intent(CallScreeningService.SERVICE_INTERFACE)
195                 .setPackage(packageName);
196         List<ResolveInfo> entries = context.getPackageManager().queryIntentServicesAsUser(
197                 intent, 0, userHandle.getIdentifier());
198         if (entries.isEmpty()) {
199             Log.i(TAG, packageName + " has no call screening service defined.");
200             return false;
201         }
202 
203         ResolveInfo entry = entries.get(0);
204         if (entry.serviceInfo == null) {
205             Log.w(TAG, packageName + " call screening service has invalid service info");
206             return false;
207         }
208 
209         if (entry.serviceInfo.permission == null || !entry.serviceInfo.permission.equals(
210                 Manifest.permission.BIND_SCREENING_SERVICE)) {
211             Log.w(TAG, "CallScreeningService must require BIND_SCREENING_SERVICE permission: " +
212                     entry.serviceInfo.packageName);
213             return false;
214         }
215 
216         ComponentName componentName =
217                 new ComponentName(entry.serviceInfo.packageName, entry.serviceInfo.name);
218         intent.setComponent(componentName);
219         if (context.bindServiceAsUser(
220                 intent,
221                 serviceConnection,
222                 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
223                 UserHandle.CURRENT)) {
224             Log.d(TAG, "bindService, found service, waiting for it to connect");
225             return true;
226         }
227 
228         return false;
229     }
230 }
231