Home | History | Annotate | Download | only in ims
      1 /*
      2  * Copyright (c) 2015, Motorola Mobility LLC
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *     - Redistributions of source code must retain the above copyright
      8  *       notice, this list of conditions and the following disclaimer.
      9  *     - Redistributions in binary form must reproduce the above copyright
     10  *       notice, this list of conditions and the following disclaimer in the
     11  *       documentation and/or other materials provided with the distribution.
     12  *     - Neither the name of Motorola Mobility nor the
     13  *       names of its contributors may be used to endorse or promote products
     14  *       derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
     26  * DAMAGE.
     27  */
     28 
     29 package com.android.service.ims;
     30 
     31 import java.util.List;
     32 
     33 import android.content.Intent;
     34 import android.content.IntentFilter;
     35 import android.os.IBinder;
     36 import android.os.RemoteException;
     37 import android.content.Context;
     38 import android.app.Service;
     39 import android.os.ServiceManager;
     40 import android.os.Handler;
     41 import android.database.ContentObserver;
     42 import android.content.BroadcastReceiver;
     43 import android.provider.Settings;
     44 import android.net.ConnectivityManager;
     45 import com.android.ims.ImsManager;
     46 import com.android.ims.ImsConnectionStateListener;
     47 import com.android.ims.ImsException;
     48 import android.telephony.SubscriptionManager;
     49 import android.telephony.ims.ImsReasonInfo;
     50 
     51 import com.android.ims.RcsManager.ResultCode;
     52 import com.android.ims.internal.IRcsService;
     53 import com.android.ims.IRcsPresenceListener;
     54 import com.android.ims.internal.IRcsPresence;
     55 
     56 import com.android.ims.internal.Logger;
     57 import com.android.internal.telephony.IccCardConstants;
     58 import com.android.internal.telephony.TelephonyIntents;
     59 
     60 import com.android.service.ims.presence.PresencePublication;
     61 import com.android.service.ims.presence.PresenceSubscriber;
     62 
     63 public class RcsService extends Service{
     64     /**
     65      * The logger
     66      */
     67     private Logger logger = Logger.getLogger(this.getClass().getName());
     68 
     69     private RcsStackAdaptor mRcsStackAdaptor = null;
     70     private PresencePublication mPublication = null;
     71     private PresenceSubscriber mSubscriber = null;
     72 
     73     private BroadcastReceiver mReceiver = null;
     74 
     75     @Override
     76     public void onCreate() {
     77         super.onCreate();
     78 
     79         logger.debug("RcsService onCreate");
     80 
     81         mRcsStackAdaptor = RcsStackAdaptor.getInstance(this);
     82 
     83         mPublication = new PresencePublication(mRcsStackAdaptor, this);
     84         mRcsStackAdaptor.getListener().setPresencePublication(mPublication);
     85 
     86         mSubscriber = new PresenceSubscriber(mRcsStackAdaptor, this);
     87         mRcsStackAdaptor.getListener().setPresenceSubscriber(mSubscriber);
     88         mPublication.setSubscriber(mSubscriber);
     89 
     90         ConnectivityManager cm = ConnectivityManager.from(this);
     91         if (cm != null) {
     92             boolean enabled = Settings.Global.getInt(getContentResolver(),
     93                     Settings.Global.MOBILE_DATA, 1) == 1;
     94             logger.debug("Mobile data enabled status: " + (enabled ? "ON" : "OFF"));
     95 
     96             onMobileDataEnabled(enabled);
     97         }
     98 
     99         // TODO: support MSIM
    100         ServiceManager.addService("rcs", mBinder);
    101 
    102         mObserver = new MobileDataContentObserver();
    103         getContentResolver().registerContentObserver(
    104                 Settings.Global.getUriFor(Settings.Global.MOBILE_DATA),
    105                 false, mObserver);
    106 
    107         mSiminfoSettingObserver = new SimInfoContentObserver();
    108         getContentResolver().registerContentObserver(
    109                 SubscriptionManager.CONTENT_URI, false, mSiminfoSettingObserver);
    110 
    111         mReceiver = new BroadcastReceiver() {
    112             @Override
    113             public void onReceive(Context context, Intent intent) {
    114                 logger.print("onReceive intent=" + intent);
    115                 if(ImsManager.ACTION_IMS_SERVICE_UP.equalsIgnoreCase(
    116                         intent.getAction())){
    117                     handleImsServiceUp();
    118                 } else if(ImsManager.ACTION_IMS_SERVICE_DOWN.equalsIgnoreCase(
    119                         intent.getAction())){
    120                     handleImsServiceDown();
    121                 } else if(TelephonyIntents.ACTION_SIM_STATE_CHANGED.equalsIgnoreCase(
    122                         intent.getAction())) {
    123                     String stateExtra = intent.getStringExtra(
    124                             IccCardConstants.INTENT_KEY_ICC_STATE);
    125                     handleSimStateChanged(stateExtra);
    126                 }
    127             }
    128         };
    129 
    130         IntentFilter statusFilter = new IntentFilter();
    131         statusFilter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
    132         statusFilter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
    133         statusFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
    134         registerReceiver(mReceiver, statusFilter);
    135     }
    136 
    137     public void handleImsServiceUp() {
    138         if(mPublication != null) {
    139             mPublication.handleImsServiceUp();
    140         }
    141 
    142         registerImsConnectionStateListener();
    143     }
    144 
    145     public void handleImsServiceDown() {
    146         if(mPublication != null) {
    147             mPublication.handleImsServiceDown();
    148         }
    149     }
    150 
    151     public void handleSimStateChanged(String state) {
    152 
    153         if(IccCardConstants.INTENT_VALUE_ICC_LOADED.equalsIgnoreCase(state)) {
    154             // ImsManager depends on a loaded SIM to get the default Voice Registration.
    155             registerImsConnectionStateListener();
    156         }
    157     }
    158 
    159 
    160     @Override
    161     public int onStartCommand(Intent intent, int flags, int startId) {
    162         logger.debug("RcsService onStartCommand");
    163 
    164         return super.onStartCommand(intent, flags, startId);
    165     }
    166 
    167     /**
    168       * Cleans up when the service is destroyed
    169       */
    170     @Override
    171     public void onDestroy() {
    172         getContentResolver().unregisterContentObserver(mObserver);
    173         getContentResolver().unregisterContentObserver(mSiminfoSettingObserver);
    174         if (mReceiver != null) {
    175             unregisterReceiver(mReceiver);
    176             mReceiver = null;
    177         }
    178 
    179         mRcsStackAdaptor.finish();
    180         mPublication.finish();
    181         mPublication = null;
    182         mSubscriber = null;
    183 
    184         logger.debug("RcsService onDestroy");
    185         super.onDestroy();
    186     }
    187 
    188     public PresencePublication getPublication() {
    189         return mPublication;
    190     }
    191 
    192     public PresenceSubscriber getPresenceSubscriber(){
    193         return mSubscriber;
    194     }
    195 
    196     IRcsPresence.Stub mIRcsPresenceImpl = new IRcsPresence.Stub(){
    197         /**
    198          * Asyncrhonously request the latest capability for a given contact list.
    199          * The result will be saved to DB directly if the contactNumber can be found in DB.
    200          * And then send intent com.android.ims.presence.CAPABILITY_STATE_CHANGED to notify it.
    201          * @param contactsNumber the contact list which will request capability.
    202          *                       Currently only support phone number.
    203          * @param listener the listener to get the response.
    204          * @return the resultCode which is defined by ResultCode.
    205          * @note framework uses only.
    206          * @hide
    207          */
    208         public int requestCapability(List<String> contactsNumber,
    209             IRcsPresenceListener listener){
    210             logger.debug("calling requestCapability");
    211             if(mSubscriber == null){
    212                 logger.debug("requestCapability, mPresenceSubscriber == null");
    213                 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
    214             }
    215 
    216             return mSubscriber.requestCapability(contactsNumber, listener);
    217          }
    218 
    219         /**
    220          * Asyncrhonously request the latest presence for a given contact.
    221          * The result will be saved to DB directly if it can be found in DB. And then send intent
    222          * com.android.ims.presence.AVAILABILITY_STATE_CHANGED to notify it.
    223          * @param contactNumber the contact which will request available.
    224          *                       Currently only support phone number.
    225          * @param listener the listener to get the response.
    226          * @return the resultCode which is defined by ResultCode.
    227          * @note framework uses only.
    228          * @hide
    229          */
    230         public int requestAvailability(String contactNumber, IRcsPresenceListener listener){
    231             if(mSubscriber == null){
    232                 logger.error("requestAvailability, mPresenceSubscriber is null");
    233                 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
    234             }
    235 
    236             // check availability cache (in RAM).
    237             return mSubscriber.requestAvailability(contactNumber, listener, false);
    238         }
    239 
    240         /**
    241          * Same as requestAvailability. but requestAvailability will consider throttle to avoid too
    242          * fast call. Which means it will not send the request to network in next 60s for the same
    243          * request.
    244          * The error code SUBSCRIBE_TOO_FREQUENTLY will be returned under the case.
    245          * But for this funcation it will always send the request to network.
    246          *
    247          * @see IRcsPresenceListener
    248          * @see RcsManager.ResultCode
    249          * @see ResultCode.SUBSCRIBE_TOO_FREQUENTLY
    250          */
    251         public int requestAvailabilityNoThrottle(String contactNumber,
    252                 IRcsPresenceListener listener) {
    253             if(mSubscriber == null){
    254                 logger.error("requestAvailabilityNoThrottle, mPresenceSubscriber is null");
    255                 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
    256             }
    257 
    258             // check availability cache (in RAM).
    259             return mSubscriber.requestAvailability(contactNumber, listener, true);
    260         }
    261 
    262         public int getPublishState() throws RemoteException {
    263             return mPublication.getPublishState();
    264         }
    265     };
    266 
    267     @Override
    268     public IBinder onBind(Intent arg0) {
    269         return mBinder;
    270     }
    271 
    272     /**
    273      * Receives notifications when Mobile data is enabled or disabled.
    274      */
    275     private class MobileDataContentObserver extends ContentObserver {
    276         public MobileDataContentObserver() {
    277             super(new Handler());
    278         }
    279 
    280         @Override
    281         public void onChange(final boolean selfChange) {
    282             boolean enabled = Settings.Global.getInt(getContentResolver(),
    283                     Settings.Global.MOBILE_DATA, 1) == 1;
    284             logger.debug("Mobile data enabled status: " + (enabled ? "ON" : "OFF"));
    285             onMobileDataEnabled(enabled);
    286         }
    287     }
    288 
    289     /** Observer to get notified when Mobile data enabled status changes */
    290     private MobileDataContentObserver mObserver;
    291 
    292     private void onMobileDataEnabled(final boolean enabled) {
    293         logger.debug("Enter onMobileDataEnabled: " + enabled);
    294         Thread thread = new Thread(new Runnable() {
    295             @Override
    296             public void run() {
    297                 try{
    298                     if(mPublication != null){
    299                         mPublication.onMobileDataChanged(enabled);
    300                         return;
    301                     }
    302                 }catch(Exception e){
    303                     logger.error("Exception onMobileDataEnabled:", e);
    304                 }
    305             }
    306         }, "onMobileDataEnabled thread");
    307 
    308         thread.start();
    309     }
    310 
    311 
    312     private SimInfoContentObserver mSiminfoSettingObserver;
    313 
    314     /**
    315      * Receives notifications when the TelephonyProvider is changed.
    316      */
    317     private class SimInfoContentObserver extends ContentObserver {
    318         public SimInfoContentObserver() {
    319             super(new Handler());
    320         }
    321 
    322         @Override
    323         public void onChange(final boolean selfChange) {
    324             ImsManager imsManager = ImsManager.getInstance(RcsService.this,
    325                     SubscriptionManager.getDefaultVoicePhoneId());
    326             if (imsManager == null) {
    327                 return;
    328             }
    329             boolean enabled = imsManager.isVtEnabledByUser();
    330              logger.debug("vt enabled status: " + (enabled ? "ON" : "OFF"));
    331 
    332             onVtEnabled(enabled);
    333         }
    334     }
    335 
    336     private void onVtEnabled(boolean enabled) {
    337         if(mPublication != null){
    338             mPublication.onVtEnabled(enabled);
    339         }
    340     }
    341 
    342     private final IRcsService.Stub mBinder = new IRcsService.Stub() {
    343         /**
    344          * return true if the rcs service is ready for use.
    345          */
    346         public boolean isRcsServiceAvailable(){
    347             logger.debug("calling isRcsServiceAvailable");
    348             if(mRcsStackAdaptor == null){
    349                 return false;
    350             }
    351 
    352             return mRcsStackAdaptor.isImsEnableState();
    353         }
    354 
    355         /**
    356          * Gets the presence interface.
    357          *
    358          * @see IRcsPresence
    359          */
    360         public IRcsPresence getRcsPresenceInterface(){
    361             return mIRcsPresenceImpl;
    362         }
    363     };
    364 
    365     void registerImsConnectionStateListener() {
    366         try {
    367             ImsManager imsManager = ImsManager.getInstance(this,
    368                     SubscriptionManager.getDefaultVoicePhoneId());
    369             if (imsManager != null) {
    370                 imsManager.addRegistrationListener(mImsConnectionStateListener);
    371             }
    372         } catch (ImsException e) {
    373             logger.error("addRegistrationListener exception=", e);
    374         }
    375     }
    376 
    377     private ImsConnectionStateListener mImsConnectionStateListener =
    378         new ImsConnectionStateListener() {
    379             @Override
    380             public void onImsConnected(int imsRadioTech) {
    381                 logger.debug("onImsConnected imsRadioTech=" + imsRadioTech);
    382                 if(mRcsStackAdaptor != null) {
    383                     mRcsStackAdaptor.checkSubService();
    384                 }
    385 
    386                 if(mPublication != null) {
    387                     mPublication.onImsConnected();
    388                 }
    389             }
    390 
    391             @Override
    392             public void onImsDisconnected(ImsReasonInfo imsReasonInfo) {
    393                 logger.debug("onImsDisconnected");
    394                 if(mPublication != null) {
    395                     mPublication.onImsDisconnected();
    396                 }
    397             }
    398 
    399             @Override
    400             public void onFeatureCapabilityChanged(final int serviceClass,
    401                     final int[] enabledFeatures, final int[] disabledFeatures) {
    402                 logger.debug("onFeatureCapabilityChanged");
    403                 if(mPublication != null) {
    404                     mPublication.onFeatureCapabilityChanged(serviceClass, enabledFeatures, disabledFeatures);
    405                 }
    406             }
    407         };
    408 }
    409 
    410