1 /*
2  * Copyright (C) 2017 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.internal.telephony;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.pm.PackageManager;
22 import android.content.pm.ResolveInfo;
23 import android.os.Binder;
24 import android.os.RemoteException;
25 import android.service.carrier.CarrierMessagingService;
26 import android.service.carrier.ICarrierMessagingCallback;
27 import android.service.carrier.ICarrierMessagingService;
28 import android.service.carrier.MessagePdu;
29 import android.telephony.CarrierMessagingServiceManager;
30 import android.telephony.Rlog;
31 
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.internal.telephony.uicc.UiccCard;
34 import com.android.internal.telephony.uicc.UiccController;
35 
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.List;
39 import java.util.Optional;
40 
41 /**
42  * Filters incoming SMS with carrier services.
43  * <p> A new instance must be created for filtering each message.
44  */
45 public class CarrierServicesSmsFilter {
46     protected static final boolean DBG = true;
47 
48     private final Context mContext;
49     private final Phone mPhone;
50     private final byte[][] mPdus;
51     private final int mDestPort;
52     private final String mPduFormat;
53     private final CarrierServicesSmsFilterCallbackInterface mCarrierServicesSmsFilterCallback;
54     private final String mLogTag;
55 
56     @VisibleForTesting
CarrierServicesSmsFilter( Context context, Phone phone, byte[][] pdus, int destPort, String pduFormat, CarrierServicesSmsFilterCallbackInterface carrierServicesSmsFilterCallback, String logTag)57     public CarrierServicesSmsFilter(
58             Context context,
59             Phone phone,
60             byte[][] pdus,
61             int destPort,
62             String pduFormat,
63             CarrierServicesSmsFilterCallbackInterface carrierServicesSmsFilterCallback,
64             String logTag) {
65         mContext = context;
66         mPhone = phone;
67         mPdus = pdus;
68         mDestPort = destPort;
69         mPduFormat = pduFormat;
70         mCarrierServicesSmsFilterCallback = carrierServicesSmsFilterCallback;
71         mLogTag = logTag;
72     }
73 
74     /**
75      * @return {@code true} if the SMS was handled by carrier services.
76      */
77     @VisibleForTesting
filter()78     public boolean filter() {
79         Optional<String> carrierAppForFiltering = getCarrierAppPackageForFiltering();
80         List<String> smsFilterPackages = new ArrayList<>();
81         if (carrierAppForFiltering.isPresent()) {
82             smsFilterPackages.add(carrierAppForFiltering.get());
83         }
84         String carrierImsPackage = CarrierSmsUtils.getCarrierImsPackageForIntent(mContext, mPhone,
85                 new Intent(CarrierMessagingService.SERVICE_INTERFACE));
86         if (carrierImsPackage != null) {
87             smsFilterPackages.add(carrierImsPackage);
88         }
89         FilterAggregator filterAggregator = new FilterAggregator(smsFilterPackages.size());
90         for (String smsFilterPackage : smsFilterPackages) {
91             filterWithPackage(smsFilterPackage, filterAggregator);
92         }
93         boolean handled = smsFilterPackages.size() > 0;
94         return handled;
95     }
96 
getCarrierAppPackageForFiltering()97     private Optional<String> getCarrierAppPackageForFiltering() {
98         List<String> carrierPackages = null;
99         UiccCard card = UiccController.getInstance().getUiccCard(mPhone.getPhoneId());
100         if (card != null) {
101             carrierPackages = card.getCarrierPackageNamesForIntent(
102                     mContext.getPackageManager(),
103                     new Intent(CarrierMessagingService.SERVICE_INTERFACE));
104         } else {
105             Rlog.e(mLogTag, "UiccCard not initialized.");
106         }
107         if (carrierPackages != null && carrierPackages.size() == 1) {
108             log("Found carrier package.");
109             return Optional.of(carrierPackages.get(0));
110         }
111 
112         // It is possible that carrier app is not present as a CarrierPackage, but instead as a
113         // system app
114         List<String> systemPackages =
115                 getSystemAppForIntent(new Intent(CarrierMessagingService.SERVICE_INTERFACE));
116 
117         if (systemPackages != null && systemPackages.size() == 1) {
118             log("Found system package.");
119             return Optional.of(systemPackages.get(0));
120         }
121         logv("Unable to find carrier package: " + carrierPackages
122                 + ", nor systemPackages: " + systemPackages);
123         return Optional.empty();
124     }
125 
filterWithPackage(String packageName, FilterAggregator filterAggregator)126     private void filterWithPackage(String packageName, FilterAggregator filterAggregator) {
127         CarrierSmsFilter smsFilter = new CarrierSmsFilter(mPdus, mDestPort, mPduFormat);
128         CarrierSmsFilterCallback smsFilterCallback =
129                 new CarrierSmsFilterCallback(filterAggregator, smsFilter);
130         smsFilter.filterSms(packageName, smsFilterCallback);
131     }
132 
getSystemAppForIntent(Intent intent)133     private List<String> getSystemAppForIntent(Intent intent) {
134         List<String> packages = new ArrayList<String>();
135         PackageManager packageManager = mContext.getPackageManager();
136         List<ResolveInfo> receivers = packageManager.queryIntentServices(intent, 0);
137         String carrierFilterSmsPerm = "android.permission.CARRIER_FILTER_SMS";
138 
139         for (ResolveInfo info : receivers) {
140             if (info.serviceInfo == null) {
141                 loge("Can't get service information from " + info);
142                 continue;
143             }
144             String packageName = info.serviceInfo.packageName;
145             if (packageManager.checkPermission(carrierFilterSmsPerm, packageName)
146                     == packageManager.PERMISSION_GRANTED) {
147                 packages.add(packageName);
148                 if (DBG) log("getSystemAppForIntent: added package " + packageName);
149             }
150         }
151         return packages;
152     }
153 
log(String message)154     private void log(String message) {
155         Rlog.d(mLogTag, message);
156     }
157 
loge(String message)158     private void loge(String message) {
159         Rlog.e(mLogTag, message);
160     }
161 
logv(String message)162     private void logv(String message) {
163         Rlog.e(mLogTag, message);
164     }
165 
166     /**
167      * Result of filtering SMS is returned in this callback.
168      */
169     @VisibleForTesting
170     public interface CarrierServicesSmsFilterCallbackInterface {
onFilterComplete(int result)171         void onFilterComplete(int result);
172     }
173 
174     /**
175      * Asynchronously binds to the carrier messaging service, and filters out the message if
176      * instructed to do so by the carrier messaging service. A new instance must be used for every
177      * message.
178      */
179     private final class CarrierSmsFilter extends CarrierMessagingServiceManager {
180         private final byte[][] mPdus;
181         private final int mDestPort;
182         private final String mSmsFormat;
183         // Instantiated in filterSms.
184         private volatile CarrierSmsFilterCallback mSmsFilterCallback;
185 
CarrierSmsFilter(byte[][] pdus, int destPort, String smsFormat)186         CarrierSmsFilter(byte[][] pdus, int destPort, String smsFormat) {
187             mPdus = pdus;
188             mDestPort = destPort;
189             mSmsFormat = smsFormat;
190         }
191 
192         /**
193          * Attempts to bind to a {@link ICarrierMessagingService}. Filtering is initiated
194          * asynchronously once the service is ready using {@link #onServiceReady}.
195          */
filterSms(String carrierPackageName, CarrierSmsFilterCallback smsFilterCallback)196         void filterSms(String carrierPackageName, CarrierSmsFilterCallback smsFilterCallback) {
197             mSmsFilterCallback = smsFilterCallback;
198             if (!bindToCarrierMessagingService(mContext, carrierPackageName)) {
199                 loge("bindService() for carrier messaging service failed");
200                 smsFilterCallback.onFilterComplete(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
201             } else {
202                 logv("bindService() for carrier messaging service succeeded");
203             }
204         }
205 
206         /**
207          * Invokes the {@code carrierMessagingService} to filter messages. The filtering result is
208          * delivered to {@code smsFilterCallback}.
209          */
210         @Override
onServiceReady(ICarrierMessagingService carrierMessagingService)211         protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
212             try {
213                 carrierMessagingService.filterSms(
214                         new MessagePdu(Arrays.asList(mPdus)), mSmsFormat, mDestPort,
215                         mPhone.getSubId(), mSmsFilterCallback);
216             } catch (RemoteException e) {
217                 loge("Exception filtering the SMS: " + e);
218                 mSmsFilterCallback.onFilterComplete(
219                         CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
220             }
221         }
222     }
223 
224     /**
225      * A callback used to notify the platform of the carrier messaging app filtering result. Once
226      * the result is ready, the carrier messaging service connection is disposed.
227      */
228     private final class CarrierSmsFilterCallback extends ICarrierMessagingCallback.Stub {
229         private final FilterAggregator mFilterAggregator;
230         private final CarrierMessagingServiceManager mCarrierMessagingServiceManager;
231 
CarrierSmsFilterCallback(FilterAggregator filterAggregator, CarrierMessagingServiceManager carrierMessagingServiceManager)232         CarrierSmsFilterCallback(FilterAggregator filterAggregator,
233                                  CarrierMessagingServiceManager carrierMessagingServiceManager) {
234             mFilterAggregator = filterAggregator;
235             mCarrierMessagingServiceManager = carrierMessagingServiceManager;
236         }
237 
238         /**
239          * This method should be called only once.
240          */
241         @Override
onFilterComplete(int result)242         public void onFilterComplete(int result) {
243             mCarrierMessagingServiceManager.disposeConnection(mContext);
244             mFilterAggregator.onFilterComplete(result);
245         }
246 
247         @Override
onSendSmsComplete(int result, int messageRef)248         public void onSendSmsComplete(int result, int messageRef) {
249             loge("Unexpected onSendSmsComplete call with result: " + result);
250         }
251 
252         @Override
onSendMultipartSmsComplete(int result, int[] messageRefs)253         public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
254             loge("Unexpected onSendMultipartSmsComplete call with result: " + result);
255         }
256 
257         @Override
onSendMmsComplete(int result, byte[] sendConfPdu)258         public void onSendMmsComplete(int result, byte[] sendConfPdu) {
259             loge("Unexpected onSendMmsComplete call with result: " + result);
260         }
261 
262         @Override
onDownloadMmsComplete(int result)263         public void onDownloadMmsComplete(int result) {
264             loge("Unexpected onDownloadMmsComplete call with result: " + result);
265         }
266     }
267 
268     private final class FilterAggregator {
269         private final Object mFilterLock = new Object();
270         private int mNumPendingFilters;
271         private int mFilterResult;
272 
FilterAggregator(int numFilters)273         FilterAggregator(int numFilters) {
274             mNumPendingFilters = numFilters;
275             mFilterResult = CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT;
276         }
277 
onFilterComplete(int result)278         void onFilterComplete(int result) {
279             synchronized (mFilterLock) {
280                 mNumPendingFilters--;
281                 combine(result);
282                 if (mNumPendingFilters == 0) {
283                     // Calling identity was the CarrierMessagingService in this callback, change it
284                     // back to ours.
285                     long token = Binder.clearCallingIdentity();
286                     try {
287                         mCarrierServicesSmsFilterCallback.onFilterComplete(mFilterResult);
288                     } finally {
289                         // return back to the CarrierMessagingService, restore the calling identity.
290                         Binder.restoreCallingIdentity(token);
291                     }
292                 }
293             }
294         }
295 
combine(int result)296         private void combine(int result) {
297             mFilterResult = mFilterResult | result;
298         }
299     }
300 }
301