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.Handler;
25 import android.os.Message;
26 import android.service.carrier.CarrierMessagingService;
27 import android.service.carrier.CarrierMessagingServiceWrapper;
28 import android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper;
29 import android.service.carrier.MessagePdu;
30 import android.util.LocalLog;
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 import com.android.telephony.Rlog;
36 
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Optional;
42 import java.util.Set;
43 
44 /**
45  * Filters incoming SMS with carrier services.
46  * <p> A new instance must be created for filtering each message.
47  */
48 public class CarrierServicesSmsFilter {
49     protected static final boolean DBG = true;
50     /** onFilterComplete is not called. */
51     public static final int EVENT_ON_FILTER_COMPLETE_NOT_CALLED = 1;
52 
53     /** onFilterComplete timeout. */
54     public static final int FILTER_COMPLETE_TIMEOUT_MS = 10 * 60 * 1000; //10 minutes
55 
56     private final Context mContext;
57     private final Phone mPhone;
58     private final byte[][] mPdus;
59     private final int mDestPort;
60     private final String mPduFormat;
61     private final CarrierServicesSmsFilterCallbackInterface mCarrierServicesSmsFilterCallback;
62     private final String mLogTag;
63     private final CallbackTimeoutHandler mCallbackTimeoutHandler;
64     private final LocalLog mLocalLog;
65     private FilterAggregator mFilterAggregator;
66 
67     @VisibleForTesting
CarrierServicesSmsFilter( Context context, Phone phone, byte[][] pdus, int destPort, String pduFormat, CarrierServicesSmsFilterCallbackInterface carrierServicesSmsFilterCallback, String logTag, LocalLog localLog)68     public CarrierServicesSmsFilter(
69             Context context,
70             Phone phone,
71             byte[][] pdus,
72             int destPort,
73             String pduFormat,
74             CarrierServicesSmsFilterCallbackInterface carrierServicesSmsFilterCallback,
75             String logTag,
76             LocalLog localLog) {
77         mContext = context;
78         mPhone = phone;
79         mPdus = pdus;
80         mDestPort = destPort;
81         mPduFormat = pduFormat;
82         mCarrierServicesSmsFilterCallback = carrierServicesSmsFilterCallback;
83         mLogTag = logTag;
84         mCallbackTimeoutHandler = new CallbackTimeoutHandler();
85         mLocalLog = localLog;
86     }
87 
88     /**
89      * @return {@code true} if the SMS was handled by carrier services.
90      */
91     @VisibleForTesting
filter()92     public boolean filter() {
93         Optional<String> carrierAppForFiltering = getCarrierAppPackageForFiltering();
94         List<String> smsFilterPackages = new ArrayList<>();
95         if (carrierAppForFiltering.isPresent()) {
96             smsFilterPackages.add(carrierAppForFiltering.get());
97         }
98         String carrierImsPackage = CarrierSmsUtils.getCarrierImsPackageForIntent(mContext, mPhone,
99                 new Intent(CarrierMessagingService.SERVICE_INTERFACE));
100         if (carrierImsPackage != null) {
101             smsFilterPackages.add(carrierImsPackage);
102         }
103 
104         if (mFilterAggregator != null) {
105             String errMsg = "Cannot reuse the same CarrierServiceSmsFilter object for filtering.";
106             loge(errMsg);
107             throw new RuntimeException(errMsg);
108         }
109 
110         int numPackages = smsFilterPackages.size();
111         if (numPackages > 0) {
112             mFilterAggregator = new FilterAggregator(numPackages);
113             //start the timer
114             mCallbackTimeoutHandler.sendMessageDelayed(mCallbackTimeoutHandler
115                             .obtainMessage(EVENT_ON_FILTER_COMPLETE_NOT_CALLED),
116                     FILTER_COMPLETE_TIMEOUT_MS);
117             for (String smsFilterPackage : smsFilterPackages) {
118                 filterWithPackage(smsFilterPackage, mFilterAggregator);
119             }
120             return true;
121         } else {
122             return false;
123         }
124     }
125 
getCarrierAppPackageForFiltering()126     private Optional<String> getCarrierAppPackageForFiltering() {
127         List<String> carrierPackages = null;
128         UiccCard card = UiccController.getInstance().getUiccCard(mPhone.getPhoneId());
129         if (card != null) {
130             carrierPackages = card.getCarrierPackageNamesForIntent(
131                     mContext.getPackageManager(),
132                     new Intent(CarrierMessagingService.SERVICE_INTERFACE));
133         } else {
134             Rlog.e(mLogTag, "UiccCard not initialized.");
135         }
136         if (carrierPackages != null && carrierPackages.size() == 1) {
137             log("Found carrier package.");
138             return Optional.of(carrierPackages.get(0));
139         }
140 
141         // It is possible that carrier app is not present as a CarrierPackage, but instead as a
142         // system app
143         List<String> systemPackages =
144                 getSystemAppForIntent(new Intent(CarrierMessagingService.SERVICE_INTERFACE));
145 
146         if (systemPackages != null && systemPackages.size() == 1) {
147             log("Found system package.");
148             return Optional.of(systemPackages.get(0));
149         }
150         logv("Unable to find carrier package: " + carrierPackages
151                 + ", nor systemPackages: " + systemPackages);
152         return Optional.empty();
153     }
154 
filterWithPackage(String packageName, FilterAggregator filterAggregator)155     private void filterWithPackage(String packageName, FilterAggregator filterAggregator) {
156         CarrierSmsFilter smsFilter = new CarrierSmsFilter(mPdus, mDestPort, mPduFormat);
157         CarrierSmsFilterCallback smsFilterCallback =
158                 new CarrierSmsFilterCallback(filterAggregator, smsFilter);
159         filterAggregator.addToCallbacks(smsFilterCallback);
160 
161         smsFilter.filterSms(packageName, smsFilterCallback);
162     }
163 
getSystemAppForIntent(Intent intent)164     private List<String> getSystemAppForIntent(Intent intent) {
165         List<String> packages = new ArrayList<String>();
166         PackageManager packageManager = mContext.getPackageManager();
167         List<ResolveInfo> receivers = packageManager.queryIntentServices(intent, 0);
168         String carrierFilterSmsPerm = "android.permission.CARRIER_FILTER_SMS";
169 
170         for (ResolveInfo info : receivers) {
171             if (info.serviceInfo == null) {
172                 loge("Can't get service information from " + info);
173                 continue;
174             }
175             String packageName = info.serviceInfo.packageName;
176             if (packageManager.checkPermission(carrierFilterSmsPerm, packageName)
177                     == packageManager.PERMISSION_GRANTED) {
178                 packages.add(packageName);
179                 if (DBG) log("getSystemAppForIntent: added package " + packageName);
180             }
181         }
182         return packages;
183     }
184 
log(String message)185     private void log(String message) {
186         Rlog.d(mLogTag, message);
187     }
188 
loge(String message)189     private void loge(String message) {
190         Rlog.e(mLogTag, message);
191     }
192 
logv(String message)193     private void logv(String message) {
194         Rlog.v(mLogTag, message);
195     }
196 
197     /**
198      * Result of filtering SMS is returned in this callback.
199      */
200     @VisibleForTesting
201     public interface CarrierServicesSmsFilterCallbackInterface {
onFilterComplete(int result)202         void onFilterComplete(int result);
203     }
204 
205     /**
206      * Asynchronously binds to the carrier messaging service, and filters out the message if
207      * instructed to do so by the carrier messaging service. A new instance must be used for every
208      * message.
209      */
210     private final class CarrierSmsFilter extends CarrierMessagingServiceWrapper {
211         private final byte[][] mPdus;
212         private final int mDestPort;
213         private final String mSmsFormat;
214         // Instantiated in filterSms.
215         private volatile CarrierSmsFilterCallback mSmsFilterCallback;
216 
CarrierSmsFilter(byte[][] pdus, int destPort, String smsFormat)217         CarrierSmsFilter(byte[][] pdus, int destPort, String smsFormat) {
218             mPdus = pdus;
219             mDestPort = destPort;
220             mSmsFormat = smsFormat;
221         }
222 
223         /**
224          * Attempts to bind to a {@link CarrierMessagingService}. Filtering is initiated
225          * asynchronously once the service is ready using {@link #onServiceReady()}.
226          */
filterSms(String carrierPackageName, CarrierSmsFilterCallback smsFilterCallback)227         void filterSms(String carrierPackageName, CarrierSmsFilterCallback smsFilterCallback) {
228             mSmsFilterCallback = smsFilterCallback;
229             if (!bindToCarrierMessagingService(mContext, carrierPackageName)) {
230                 loge("bindService() for carrier messaging service failed");
231                 smsFilterCallback.onFilterComplete(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
232             } else {
233                 logv("bindService() for carrier messaging service succeeded");
234             }
235         }
236 
237         /**
238          * Invokes the {@code carrierMessagingService} to filter messages. The filtering result is
239          * delivered to {@code smsFilterCallback}.
240          */
241         @Override
onServiceReady()242         public void onServiceReady() {
243             try {
244                 log("onServiceReady: calling filterSms");
245                 filterSms(new MessagePdu(Arrays.asList(mPdus)), mSmsFormat, mDestPort,
246                         mPhone.getSubId(), mSmsFilterCallback);
247             } catch (RuntimeException e) {
248                 loge("Exception filtering the SMS: " + e);
249                 mSmsFilterCallback.onFilterComplete(
250                         CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
251             }
252         }
253     }
254 
255     /**
256      * A callback used to notify the platform of the carrier messaging app filtering result. Once
257      * the result is ready, the carrier messaging service connection is disposed.
258      */
259     private final class CarrierSmsFilterCallback extends CarrierMessagingCallbackWrapper {
260         private final FilterAggregator mFilterAggregator;
261         private final CarrierMessagingServiceWrapper mCarrierMessagingServiceWrapper;
262         private boolean mIsOnFilterCompleteCalled;
263 
CarrierSmsFilterCallback(FilterAggregator filterAggregator, CarrierMessagingServiceWrapper carrierMessagingServiceWrapper)264         CarrierSmsFilterCallback(FilterAggregator filterAggregator,
265                 CarrierMessagingServiceWrapper carrierMessagingServiceWrapper) {
266             mFilterAggregator = filterAggregator;
267             mCarrierMessagingServiceWrapper = carrierMessagingServiceWrapper;
268             mIsOnFilterCompleteCalled = false;
269         }
270 
271         /**
272          * This method should be called only once.
273          */
274         @Override
onFilterComplete(int result)275         public void onFilterComplete(int result) {
276             log("onFilterComplete called with result: " + result);
277             // in the case that timeout has already passed and triggered, but the initial callback
278             // is run afterwards, we should not follow through
279             if (!mIsOnFilterCompleteCalled) {
280                 mIsOnFilterCompleteCalled = true;
281                 mCarrierMessagingServiceWrapper.disposeConnection(mContext);
282                 mFilterAggregator.onFilterComplete(result);
283             }
284         }
285 
286         @Override
onSendSmsComplete(int result, int messageRef)287         public void onSendSmsComplete(int result, int messageRef) {
288             loge("Unexpected onSendSmsComplete call with result: " + result);
289         }
290 
291         @Override
onSendMultipartSmsComplete(int result, int[] messageRefs)292         public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
293             loge("Unexpected onSendMultipartSmsComplete call with result: " + result);
294         }
295 
296         @Override
onSendMmsComplete(int result, byte[] sendConfPdu)297         public void onSendMmsComplete(int result, byte[] sendConfPdu) {
298             loge("Unexpected onSendMmsComplete call with result: " + result);
299         }
300 
301         @Override
onDownloadMmsComplete(int result)302         public void onDownloadMmsComplete(int result) {
303             loge("Unexpected onDownloadMmsComplete call with result: " + result);
304         }
305     }
306 
307     private final class FilterAggregator {
308         private final Object mFilterLock = new Object();
309         private int mNumPendingFilters;
310         private final Set<CarrierSmsFilterCallback> mCallbacks;
311         private int mFilterResult;
312 
FilterAggregator(int numFilters)313         FilterAggregator(int numFilters) {
314             mNumPendingFilters = numFilters;
315             mCallbacks = new HashSet<>();
316             mFilterResult = CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT;
317         }
318 
onFilterComplete(int result)319         void onFilterComplete(int result) {
320             synchronized (mFilterLock) {
321                 mNumPendingFilters--;
322                 combine(result);
323                 if (mNumPendingFilters == 0) {
324                     // Calling identity was the CarrierMessagingService in this callback, change it
325                     // back to ours.
326                     long token = Binder.clearCallingIdentity();
327                     try {
328                         mCarrierServicesSmsFilterCallback.onFilterComplete(mFilterResult);
329                     } finally {
330                         // return back to the CarrierMessagingService, restore the calling identity.
331                         Binder.restoreCallingIdentity(token);
332                     }
333                     //all onFilterCompletes called before timeout has triggered
334                     //remove the pending message
335                     log("onFilterComplete: called successfully with result = " + result);
336                     mCallbackTimeoutHandler.removeMessages(EVENT_ON_FILTER_COMPLETE_NOT_CALLED);
337                 } else {
338                     log("onFilterComplete: waiting for pending filters " + mNumPendingFilters);
339                 }
340             }
341         }
342 
combine(int result)343         private void combine(int result) {
344             mFilterResult = mFilterResult | result;
345         }
346 
addToCallbacks(CarrierSmsFilterCallback callback)347         private void addToCallbacks(CarrierSmsFilterCallback callback) {
348             mCallbacks.add(callback);
349         }
350 
351     }
352 
353     protected final class CallbackTimeoutHandler extends Handler {
354 
355         private static final boolean DBG = true;
356 
357         @Override
handleMessage(Message msg)358         public void handleMessage(Message msg) {
359             if (DBG) {
360                 log("CallbackTimeoutHandler handleMessage(" + msg.what + ")");
361             }
362 
363             switch(msg.what) {
364                 case EVENT_ON_FILTER_COMPLETE_NOT_CALLED:
365                     mLocalLog.log("CarrierServicesSmsFilter: onFilterComplete timeout: not"
366                             + " called before " + FILTER_COMPLETE_TIMEOUT_MS + " milliseconds.");
367                     handleFilterCallbacksTimeout();
368                     break;
369             }
370         }
371 
handleFilterCallbacksTimeout()372         private void handleFilterCallbacksTimeout() {
373             for (CarrierSmsFilterCallback callback : mFilterAggregator.mCallbacks) {
374                 log("handleFilterCallbacksTimeout: calling onFilterComplete");
375                 callback.onFilterComplete(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
376             }
377         }
378     }
379 }
380