Home | History | Annotate | Download | only in dataconnection
      1 /*
      2  * Copyright 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.internal.telephony.dataconnection;
     18 
     19 import static android.telephony.AccessNetworkConstants.TransportType.WLAN;
     20 import static android.telephony.AccessNetworkConstants.TransportType.WWAN;
     21 
     22 import android.annotation.NonNull;
     23 import android.app.AppOpsManager;
     24 import android.content.ComponentName;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.ServiceConnection;
     28 import android.content.pm.IPackageManager;
     29 import android.content.pm.PackageManager;
     30 import android.content.pm.ResolveInfo;
     31 import android.net.LinkProperties;
     32 import android.os.AsyncResult;
     33 import android.os.Handler;
     34 import android.os.IBinder;
     35 import android.os.Message;
     36 import android.os.PersistableBundle;
     37 import android.os.RegistrantList;
     38 import android.os.RemoteException;
     39 import android.os.ServiceManager;
     40 import android.telephony.CarrierConfigManager;
     41 import android.telephony.Rlog;
     42 import android.telephony.data.DataCallResponse;
     43 import android.telephony.data.DataProfile;
     44 import android.telephony.data.DataService;
     45 import android.telephony.data.DataServiceCallback;
     46 import android.telephony.data.IDataService;
     47 import android.telephony.data.IDataServiceCallback;
     48 import android.text.TextUtils;
     49 
     50 import com.android.internal.telephony.Phone;
     51 
     52 import java.util.HashSet;
     53 import java.util.List;
     54 import java.util.Map;
     55 import java.util.Set;
     56 import java.util.concurrent.ConcurrentHashMap;
     57 
     58 /**
     59  * Data service manager manages handling data requests and responses on data services (e.g.
     60  * Cellular data service, IWLAN data service).
     61  */
     62 public class DataServiceManager {
     63     private static final String TAG = DataServiceManager.class.getSimpleName();
     64     private static final boolean DBG = false;
     65 
     66     static final String DATA_CALL_RESPONSE = "data_call_response";
     67 
     68     private final Phone mPhone;
     69 
     70     private final CarrierConfigManager mCarrierConfigManager;
     71     private final AppOpsManager mAppOps;
     72     private final IPackageManager mPackageManager;
     73 
     74     private final int mTransportType;
     75 
     76     private boolean mBound;
     77 
     78     private IDataService mIDataService;
     79 
     80     private DataServiceManagerDeathRecipient mDeathRecipient;
     81 
     82     private final RegistrantList mServiceBindingChangedRegistrants = new RegistrantList();
     83 
     84     private final Map<IBinder, Message> mMessageMap = new ConcurrentHashMap<>();
     85 
     86     private final RegistrantList mDataCallListChangedRegistrants = new RegistrantList();
     87 
     88     // not final because it is set by the onServiceConnected method
     89     private ComponentName mComponentName;
     90 
     91     private class DataServiceManagerDeathRecipient implements IBinder.DeathRecipient {
     92         @Override
     93         public void binderDied() {
     94             // TODO: try to rebind the service.
     95             loge("DataService(" + mComponentName +  " transport type " + mTransportType
     96                     + ") died.");
     97         }
     98     }
     99 
    100     private void grantPermissionsToService(String packageName) {
    101         final String[] pkgToGrant = {packageName};
    102         try {
    103             mPackageManager.grantDefaultPermissionsToEnabledTelephonyDataServices(
    104                     pkgToGrant, mPhone.getContext().getUserId());
    105             mAppOps.setMode(AppOpsManager.OP_MANAGE_IPSEC_TUNNELS, mPhone.getContext().getUserId(),
    106                     pkgToGrant[0], AppOpsManager.MODE_ALLOWED);
    107         } catch (RemoteException e) {
    108             loge("Binder to package manager died, permission grant for DataService failed.");
    109             throw e.rethrowAsRuntimeException();
    110         }
    111     }
    112 
    113     /**
    114      * Loop through all DataServices installed on the system and revoke permissions from any that
    115      * are not currently the WWAN or WLAN data service.
    116      */
    117     private void revokePermissionsFromUnusedDataServices() {
    118         // Except the current data services from having their permissions removed.
    119         Set<String> dataServices = getAllDataServicePackageNames();
    120         for (int transportType : new int[] {WWAN, WLAN}) {
    121             dataServices.remove(getDataServicePackageName(transportType));
    122         }
    123 
    124         try {
    125             String[] dataServicesArray = new String[dataServices.size()];
    126             dataServices.toArray(dataServicesArray);
    127             mPackageManager.revokeDefaultPermissionsFromDisabledTelephonyDataServices(
    128                     dataServicesArray, mPhone.getContext().getUserId());
    129             for (String pkg : dataServices) {
    130                 mAppOps.setMode(AppOpsManager.OP_MANAGE_IPSEC_TUNNELS,
    131                         mPhone.getContext().getUserId(),
    132                         pkg, AppOpsManager.MODE_ERRORED);
    133             }
    134         } catch (RemoteException e) {
    135             loge("Binder to package manager died; failed to revoke DataService permissions.");
    136             throw e.rethrowAsRuntimeException();
    137         }
    138     }
    139 
    140     private final class CellularDataServiceConnection implements ServiceConnection {
    141         @Override
    142         public void onServiceConnected(ComponentName name, IBinder service) {
    143             if (DBG) log("onServiceConnected");
    144             mComponentName = name;
    145             mIDataService = IDataService.Stub.asInterface(service);
    146             mDeathRecipient = new DataServiceManagerDeathRecipient();
    147             mBound = true;
    148 
    149             try {
    150                 service.linkToDeath(mDeathRecipient, 0);
    151                 mIDataService.createDataServiceProvider(mPhone.getPhoneId());
    152                 mIDataService.registerForDataCallListChanged(mPhone.getPhoneId(),
    153                         new CellularDataServiceCallback());
    154             } catch (RemoteException e) {
    155                 mDeathRecipient.binderDied();
    156                 loge("Remote exception. " + e);
    157                 return;
    158             }
    159 
    160             mServiceBindingChangedRegistrants.notifyResult(true);
    161         }
    162         @Override
    163         public void onServiceDisconnected(ComponentName name) {
    164             if (DBG) log("onServiceDisconnected");
    165             mIDataService.asBinder().unlinkToDeath(mDeathRecipient, 0);
    166             mIDataService = null;
    167             mBound = false;
    168             mServiceBindingChangedRegistrants.notifyResult(false);
    169         }
    170     }
    171 
    172     private final class CellularDataServiceCallback extends IDataServiceCallback.Stub {
    173         @Override
    174         public void onSetupDataCallComplete(@DataServiceCallback.ResultCode int resultCode,
    175                                             DataCallResponse response) {
    176             if (DBG) {
    177                 log("onSetupDataCallComplete. resultCode = " + resultCode + ", response = "
    178                         + response);
    179             }
    180             Message msg = mMessageMap.remove(asBinder());
    181             if (msg != null) {
    182                 msg.getData().putParcelable(DATA_CALL_RESPONSE, response);
    183                 sendCompleteMessage(msg, resultCode);
    184             } else {
    185                 loge("Unable to find the message for setup call response.");
    186             }
    187         }
    188 
    189         @Override
    190         public void onDeactivateDataCallComplete(@DataServiceCallback.ResultCode int resultCode) {
    191             if (DBG) log("onDeactivateDataCallComplete. resultCode = " + resultCode);
    192             Message msg = mMessageMap.remove(asBinder());
    193             sendCompleteMessage(msg, resultCode);
    194         }
    195 
    196         @Override
    197         public void onSetInitialAttachApnComplete(@DataServiceCallback.ResultCode int resultCode) {
    198             if (DBG) log("onSetInitialAttachApnComplete. resultCode = " + resultCode);
    199             Message msg = mMessageMap.remove(asBinder());
    200             sendCompleteMessage(msg, resultCode);
    201         }
    202 
    203         @Override
    204         public void onSetDataProfileComplete(@DataServiceCallback.ResultCode int resultCode) {
    205             if (DBG) log("onSetDataProfileComplete. resultCode = " + resultCode);
    206             Message msg = mMessageMap.remove(asBinder());
    207             sendCompleteMessage(msg, resultCode);
    208         }
    209 
    210         @Override
    211         public void onGetDataCallListComplete(@DataServiceCallback.ResultCode int resultCode,
    212                                               List<DataCallResponse> dataCallList) {
    213             if (DBG) log("onGetDataCallListComplete. resultCode = " + resultCode);
    214             Message msg = mMessageMap.remove(asBinder());
    215             sendCompleteMessage(msg, resultCode);
    216         }
    217 
    218         @Override
    219         public void onDataCallListChanged(List<DataCallResponse> dataCallList) {
    220             mDataCallListChangedRegistrants.notifyRegistrants(
    221                     new AsyncResult(null, dataCallList, null));
    222         }
    223     }
    224 
    225     /**
    226      * Constructor
    227      *
    228      * @param phone The phone object
    229      * @param transportType The transport type. Must be a
    230      *        {@link AccessNetworkConstants.TransportType}.
    231      */
    232     public DataServiceManager(Phone phone, int transportType) {
    233         mPhone = phone;
    234         mTransportType = transportType;
    235         mBound = false;
    236         mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
    237                 Context.CARRIER_CONFIG_SERVICE);
    238         mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
    239         mAppOps = (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE);
    240         bindDataService();
    241     }
    242 
    243     private void bindDataService() {
    244         // Start by cleaning up all packages that *shouldn't* have permissions.
    245         revokePermissionsFromUnusedDataServices();
    246 
    247         String packageName = getDataServicePackageName();
    248         if (TextUtils.isEmpty(packageName)) {
    249             loge("Can't find the binding package");
    250             return;
    251         }
    252         // Then pre-emptively grant the permissions to the package we will bind.
    253         grantPermissionsToService(packageName);
    254 
    255         try {
    256             if (!mPhone.getContext().bindService(
    257                     new Intent(DataService.DATA_SERVICE_INTERFACE).setPackage(packageName),
    258                     new CellularDataServiceConnection(),
    259                     Context.BIND_AUTO_CREATE)) {
    260                 loge("Cannot bind to the data service.");
    261             }
    262         } catch (Exception e) {
    263             loge("Cannot bind to the data service. Exception: " + e);
    264         }
    265     }
    266 
    267     @NonNull
    268     private Set<String> getAllDataServicePackageNames() {
    269         // Cowardly using the public PackageManager interface here.
    270         // Note: This matches only packages that were installed on the system image. If we ever
    271         // expand the permissions model to allow CarrierPrivileged packages, then this will need
    272         // to be updated.
    273         List<ResolveInfo> dataPackages =
    274                 mPhone.getContext().getPackageManager().queryIntentServices(
    275                         new Intent(DataService.DATA_SERVICE_INTERFACE),
    276                                 PackageManager.MATCH_SYSTEM_ONLY);
    277         HashSet<String> packageNames = new HashSet<>();
    278         for (ResolveInfo info : dataPackages) {
    279             if (info.serviceInfo == null) continue;
    280             packageNames.add(info.serviceInfo.packageName);
    281         }
    282         return packageNames;
    283     }
    284 
    285     /**
    286      * Get the data service package name for our current transport type.
    287      *
    288      * @return package name of the data service package for the the current transportType.
    289      */
    290     private String getDataServicePackageName() {
    291         return getDataServicePackageName(mTransportType);
    292     }
    293 
    294     /**
    295      * Get the data service package by transport type.
    296      *
    297      * When we bind to a DataService package, we need to revoke permissions from stale
    298      * packages; we need to exclude data packages for all transport types, so we need to
    299      * to be able to query by transport type.
    300      *
    301      * @param transportType either WWAN or WLAN
    302      * @return package name of the data service package for the specified transportType.
    303      */
    304     private String getDataServicePackageName(int transportType) {
    305         String packageName;
    306         int resourceId;
    307         String carrierConfig;
    308 
    309         switch (transportType) {
    310             case WWAN:
    311                 resourceId = com.android.internal.R.string.config_wwan_data_service_package;
    312                 carrierConfig = CarrierConfigManager
    313                         .KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING;
    314                 break;
    315             case WLAN:
    316                 resourceId = com.android.internal.R.string.config_wlan_data_service_package;
    317                 carrierConfig = CarrierConfigManager
    318                         .KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING;
    319                 break;
    320             default:
    321                 throw new IllegalStateException("Transport type not WWAN or WLAN. type="
    322                         + mTransportType);
    323         }
    324 
    325         // Read package name from resource overlay
    326         packageName = mPhone.getContext().getResources().getString(resourceId);
    327 
    328         PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
    329 
    330         if (b != null) {
    331             // If carrier config overrides it, use the one from carrier config
    332             packageName = b.getString(carrierConfig, packageName);
    333         }
    334 
    335         return packageName;
    336     }
    337 
    338     private void sendCompleteMessage(Message msg, int code) {
    339         if (msg != null) {
    340             msg.arg1 = code;
    341             msg.sendToTarget();
    342         }
    343     }
    344 
    345     /**
    346      * Setup a data connection. The data service provider must implement this method to support
    347      * establishing a packet data connection. When completed or error, the service must invoke
    348      * the provided callback to notify the platform.
    349      *
    350      * @param accessNetworkType Access network type that the data call will be established on.
    351      *        Must be one of {@link AccessNetworkConstants.AccessNetworkType}.
    352      * @param dataProfile Data profile used for data call setup. See {@link DataProfile}
    353      * @param isRoaming True if the device is data roaming.
    354      * @param allowRoaming True if data roaming is allowed by the user.
    355      * @param reason The reason for data setup. Must be {@link DataService#REQUEST_REASON_NORMAL} or
    356      *        {@link DataService#REQUEST_REASON_HANDOVER}.
    357      * @param linkProperties If {@code reason} is {@link DataService#REQUEST_REASON_HANDOVER}, this
    358      *        is the link properties of the existing data connection, otherwise null.
    359      * @param onCompleteMessage The result message for this request. Null if the client does not
    360      *        care about the result.
    361      */
    362     public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
    363                               boolean allowRoaming, int reason, LinkProperties linkProperties,
    364                               Message onCompleteMessage) {
    365         if (DBG) log("setupDataCall");
    366         if (!mBound) {
    367             loge("Data service not bound.");
    368             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
    369             return;
    370         }
    371 
    372         CellularDataServiceCallback callback = null;
    373         if (onCompleteMessage != null) {
    374             callback = new CellularDataServiceCallback();
    375             mMessageMap.put(callback.asBinder(), onCompleteMessage);
    376         }
    377         try {
    378             mIDataService.setupDataCall(mPhone.getPhoneId(), accessNetworkType, dataProfile,
    379                     isRoaming, allowRoaming, reason, linkProperties, callback);
    380         } catch (RemoteException e) {
    381             loge("Cannot invoke setupDataCall on data service.");
    382             if (callback != null) {
    383                 mMessageMap.remove(callback.asBinder());
    384             }
    385             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
    386         }
    387     }
    388 
    389     /**
    390      * Deactivate a data connection. The data service provider must implement this method to
    391      * support data connection tear down. When completed or error, the service must invoke the
    392      * provided callback to notify the platform.
    393      *
    394      * @param cid Call id returned in the callback of {@link #setupDataCall(int, DataProfile,
    395      *        boolean, boolean, int, LinkProperties, Message)}
    396      * @param reason The reason for data deactivation. Must be
    397      *        {@link DataService#REQUEST_REASON_NORMAL}, {@link DataService#REQUEST_REASON_SHUTDOWN}
    398      *        or {@link DataService#REQUEST_REASON_HANDOVER}.
    399      * @param onCompleteMessage The result message for this request. Null if the client does not
    400      *        care about the result.
    401      */
    402     public void deactivateDataCall(int cid, int reason, Message onCompleteMessage) {
    403         if (DBG) log("deactivateDataCall");
    404         if (!mBound) {
    405             loge("Data service not bound.");
    406             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
    407             return;
    408         }
    409 
    410         CellularDataServiceCallback callback = null;
    411         if (onCompleteMessage != null) {
    412             callback = new CellularDataServiceCallback();
    413             mMessageMap.put(callback.asBinder(), onCompleteMessage);
    414         }
    415         try {
    416             mIDataService.deactivateDataCall(mPhone.getPhoneId(), cid, reason, callback);
    417         } catch (RemoteException e) {
    418             loge("Cannot invoke deactivateDataCall on data service.");
    419             if (callback != null) {
    420                 mMessageMap.remove(callback.asBinder());
    421             }
    422             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
    423         }
    424     }
    425 
    426     /**
    427      * Set an APN to initial attach network.
    428      *
    429      * @param dataProfile Data profile used for data call setup. See {@link DataProfile}.
    430      * @param isRoaming True if the device is data roaming.
    431      * @param onCompleteMessage The result message for this request. Null if the client does not
    432      *        care about the result.
    433      */
    434     public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
    435                                     Message onCompleteMessage) {
    436         if (DBG) log("setInitialAttachApn");
    437         if (!mBound) {
    438             loge("Data service not bound.");
    439             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
    440             return;
    441         }
    442 
    443         CellularDataServiceCallback callback = null;
    444         if (onCompleteMessage != null) {
    445             callback = new CellularDataServiceCallback();
    446             mMessageMap.put(callback.asBinder(), onCompleteMessage);
    447         }
    448         try {
    449             mIDataService.setInitialAttachApn(mPhone.getPhoneId(), dataProfile, isRoaming,
    450                     callback);
    451         } catch (RemoteException e) {
    452             loge("Cannot invoke setInitialAttachApn on data service.");
    453             if (callback != null) {
    454                 mMessageMap.remove(callback.asBinder());
    455             }
    456             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
    457         }
    458     }
    459 
    460     /**
    461      * Send current carrier's data profiles to the data service for data call setup. This is
    462      * only for CDMA carrier that can change the profile through OTA. The data service should
    463      * always uses the latest data profile sent by the framework.
    464      *
    465      * @param dps A list of data profiles.
    466      * @param isRoaming True if the device is data roaming.
    467      * @param onCompleteMessage The result message for this request. Null if the client does not
    468      *        care about the result.
    469      */
    470     public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
    471                                Message onCompleteMessage) {
    472         if (DBG) log("setDataProfile");
    473         if (!mBound) {
    474             loge("Data service not bound.");
    475             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
    476             return;
    477         }
    478 
    479         CellularDataServiceCallback callback = null;
    480         if (onCompleteMessage != null) {
    481             callback = new CellularDataServiceCallback();
    482             mMessageMap.put(callback.asBinder(), onCompleteMessage);
    483         }
    484         try {
    485             mIDataService.setDataProfile(mPhone.getPhoneId(), dps, isRoaming, callback);
    486         } catch (RemoteException e) {
    487             loge("Cannot invoke setDataProfile on data service.");
    488             if (callback != null) {
    489                 mMessageMap.remove(callback.asBinder());
    490             }
    491             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
    492         }
    493     }
    494 
    495     /**
    496      * Get the active data call list.
    497      *
    498      * @param onCompleteMessage The result message for this request. Null if the client does not
    499      *        care about the result.
    500      */
    501     public void getDataCallList(Message onCompleteMessage) {
    502         if (DBG) log("getDataCallList");
    503         if (!mBound) {
    504             loge("Data service not bound.");
    505             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
    506             return;
    507         }
    508 
    509         CellularDataServiceCallback callback = null;
    510         if (onCompleteMessage != null) {
    511             callback = new CellularDataServiceCallback();
    512             mMessageMap.put(callback.asBinder(), onCompleteMessage);
    513         }
    514         try {
    515             mIDataService.getDataCallList(mPhone.getPhoneId(), callback);
    516         } catch (RemoteException e) {
    517             loge("Cannot invoke getDataCallList on data service.");
    518             if (callback != null) {
    519                 mMessageMap.remove(callback.asBinder());
    520             }
    521             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
    522         }
    523     }
    524 
    525     /**
    526      * Register for data call list changed event.
    527      *
    528      * @param h The target to post the event message to.
    529      * @param what The event.
    530      */
    531     public void registerForDataCallListChanged(Handler h, int what) {
    532         if (h != null) {
    533             mDataCallListChangedRegistrants.addUnique(h, what, null);
    534         }
    535     }
    536 
    537     /**
    538      * Unregister for data call list changed event.
    539      *
    540      * @param h The handler
    541      */
    542     public void unregisterForDataCallListChanged(Handler h) {
    543         if (h != null) {
    544             mDataCallListChangedRegistrants.remove(h);
    545         }
    546     }
    547 
    548     /**
    549      * Register for data service binding status changed event.
    550      *
    551      * @param h The target to post the event message to.
    552      * @param what The event.
    553      * @param obj The user object.
    554      */
    555     public void registerForServiceBindingChanged(Handler h, int what, Object obj) {
    556         if (h != null) {
    557             mServiceBindingChangedRegistrants.addUnique(h, what, obj);
    558         }
    559 
    560     }
    561 
    562     /**
    563      * Unregister for data service binding status changed event.
    564      *
    565      * @param h The handler
    566      */
    567     public void unregisterForServiceBindingChanged(Handler h) {
    568         if (h != null) {
    569             mServiceBindingChangedRegistrants.remove(h);
    570         }
    571     }
    572 
    573     /**
    574      * Get the transport type. Must be a {@link AccessNetworkConstants.TransportType}.
    575      *
    576      * @return
    577      */
    578     public int getTransportType() {
    579         return mTransportType;
    580     }
    581 
    582     private void log(String s) {
    583         Rlog.d(TAG, s);
    584     }
    585 
    586     private void loge(String s) {
    587         Rlog.e(TAG, s);
    588     }
    589 
    590 }
    591