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