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