Home | History | Annotate | Download | only in telephony
      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
     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
     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 
     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 
    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 
    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 
    154     private void log(String message) {
    155         Rlog.d(mLogTag, message);
    156     }
    157 
    158     private void loge(String message) {
    159         Rlog.e(mLogTag, message);
    160     }
    161 
    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 {
    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 
    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          */
    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
    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 
    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
    242         public void onFilterComplete(int result) {
    243             mCarrierMessagingServiceManager.disposeConnection(mContext);
    244             mFilterAggregator.onFilterComplete(result);
    245         }
    246 
    247         @Override
    248         public void onSendSmsComplete(int result, int messageRef) {
    249             loge("Unexpected onSendSmsComplete call with result: " + result);
    250         }
    251 
    252         @Override
    253         public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
    254             loge("Unexpected onSendMultipartSmsComplete call with result: " + result);
    255         }
    256 
    257         @Override
    258         public void onSendMmsComplete(int result, byte[] sendConfPdu) {
    259             loge("Unexpected onSendMmsComplete call with result: " + result);
    260         }
    261 
    262         @Override
    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 
    273         FilterAggregator(int numFilters) {
    274             mNumPendingFilters = numFilters;
    275             mFilterResult = CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT;
    276         }
    277 
    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 
    296         private void combine(int result) {
    297             mFilterResult = mFilterResult | result;
    298         }
    299     }
    300 }
    301