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 android.net.Uri; 32 33 import java.util.List; 34 35 import android.content.Intent; 36 import android.app.PendingIntent; 37 import android.content.IntentFilter; 38 import android.os.IBinder; 39 import android.os.RemoteException; 40 import android.content.Context; 41 import android.app.Service; 42 import android.os.ServiceManager; 43 import android.os.Handler; 44 import android.database.ContentObserver; 45 import android.content.BroadcastReceiver; 46 import android.provider.Settings; 47 import android.net.ConnectivityManager; 48 import com.android.ims.ImsConfig.FeatureValueConstants; 49 import com.android.ims.ImsManager; 50 import com.android.ims.ImsConfig; 51 import com.android.ims.ImsReasonInfo; 52 import com.android.ims.ImsConnectionStateListener; 53 import com.android.ims.ImsServiceClass; 54 import com.android.ims.ImsException; 55 import android.telephony.SubscriptionManager; 56 57 import com.android.ims.RcsManager.ResultCode; 58 import com.android.ims.internal.IRcsService; 59 import com.android.ims.IRcsPresenceListener; 60 import com.android.ims.internal.IRcsPresence; 61 import com.android.ims.RcsPresence.PublishState; 62 63 import com.android.ims.internal.Logger; 64 import com.android.service.ims.RcsStackAdaptor; 65 66 import com.android.service.ims.presence.PresencePublication; 67 import com.android.service.ims.presence.PresenceSubscriber; 68 69 public class RcsService extends Service{ 70 /** 71 * The logger 72 */ 73 private Logger logger = Logger.getLogger(this.getClass().getName()); 74 75 private RcsStackAdaptor mRcsStackAdaptor = null; 76 private PresencePublication mPublication = null; 77 private PresenceSubscriber mSubscriber = null; 78 79 private BroadcastReceiver mReceiver = null; 80 81 @Override onCreate()82 public void onCreate() { 83 super.onCreate(); 84 85 logger.debug("RcsService onCreate"); 86 87 mRcsStackAdaptor = RcsStackAdaptor.getInstance(this); 88 89 mPublication = new PresencePublication(mRcsStackAdaptor, this); 90 mRcsStackAdaptor.getListener().setPresencePublication(mPublication); 91 92 mSubscriber = new PresenceSubscriber(mRcsStackAdaptor, this); 93 mRcsStackAdaptor.getListener().setPresenceSubscriber(mSubscriber); 94 mPublication.setSubscriber(mSubscriber); 95 96 ConnectivityManager cm = ConnectivityManager.from(this); 97 if (cm != null) { 98 boolean enabled = Settings.Global.getInt(getContentResolver(), 99 Settings.Global.MOBILE_DATA, 1) == 1; 100 logger.debug("Mobile data enabled status: " + (enabled ? "ON" : "OFF")); 101 102 onMobileDataEnabled(enabled); 103 } 104 105 // TODO: support MSIM 106 ServiceManager.addService("rcs", mBinder); 107 108 mObserver = new MobileDataContentObserver(); 109 getContentResolver().registerContentObserver( 110 Settings.Global.getUriFor(Settings.Global.MOBILE_DATA), 111 false, mObserver); 112 113 mVtSettingObserver = new VtSettingContentObserver(); 114 getContentResolver().registerContentObserver( 115 Settings.Global.getUriFor(Settings.Global.VT_IMS_ENABLED), 116 false, mVtSettingObserver); 117 118 registerImsConnectionStateListener(); 119 120 mReceiver = new BroadcastReceiver() { 121 @Override 122 public void onReceive(Context context, Intent intent) { 123 logger.print("onReceive intent=" + intent); 124 if(ImsManager.ACTION_IMS_SERVICE_UP.equalsIgnoreCase( 125 intent.getAction())){ 126 handleImsServiceUp(); 127 } else if(ImsManager.ACTION_IMS_SERVICE_DOWN.equalsIgnoreCase( 128 intent.getAction())){ 129 handleImsServiceDown(); 130 } 131 } 132 }; 133 134 IntentFilter statusFilter = new IntentFilter(); 135 statusFilter.addAction(ImsManager.ACTION_IMS_SERVICE_UP); 136 statusFilter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN); 137 registerReceiver(mReceiver, statusFilter); 138 } 139 handleImsServiceUp()140 public void handleImsServiceUp() { 141 if(mPublication != null) { 142 mPublication.handleImsServiceUp(); 143 } 144 145 registerImsConnectionStateListener(); 146 } 147 handleImsServiceDown()148 public void handleImsServiceDown() { 149 if(mPublication != null) { 150 mPublication.handleImsServiceDown(); 151 } 152 } 153 154 155 @Override onStartCommand(Intent intent, int flags, int startId)156 public int onStartCommand(Intent intent, int flags, int startId) { 157 logger.debug("RcsService onStartCommand"); 158 159 return super.onStartCommand(intent, flags, startId); 160 } 161 162 /** 163 * Cleans up when the service is destroyed 164 */ 165 @Override onDestroy()166 public void onDestroy() { 167 getContentResolver().unregisterContentObserver(mObserver); 168 getContentResolver().unregisterContentObserver(mVtSettingObserver); 169 if (mReceiver != null) { 170 unregisterReceiver(mReceiver); 171 mReceiver = null; 172 } 173 174 mRcsStackAdaptor.finish(); 175 mPublication.finish(); 176 mPublication = null; 177 mSubscriber = null; 178 179 logger.debug("RcsService onDestroy"); 180 super.onDestroy(); 181 } 182 getPublication()183 public PresencePublication getPublication() { 184 return mPublication; 185 } 186 getPresenceSubscriber()187 public PresenceSubscriber getPresenceSubscriber(){ 188 return mSubscriber; 189 } 190 191 IRcsPresence.Stub mIRcsPresenceImpl = new IRcsPresence.Stub(){ 192 /** 193 * Asyncrhonously request the latest capability for a given contact list. 194 * The result will be saved to DB directly if the contactNumber can be found in DB. 195 * And then send intent com.android.ims.presence.CAPABILITY_STATE_CHANGED to notify it. 196 * @param contactsNumber the contact list which will request capability. 197 * Currently only support phone number. 198 * @param listener the listener to get the response. 199 * @return the resultCode which is defined by ResultCode. 200 * @note framework uses only. 201 * @hide 202 */ 203 public int requestCapability(List<String> contactsNumber, 204 IRcsPresenceListener listener){ 205 logger.debug("calling requestCapability"); 206 if(mSubscriber == null){ 207 logger.debug("requestCapability, mPresenceSubscriber == null"); 208 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE; 209 } 210 211 return mSubscriber.requestCapability(contactsNumber, listener); 212 } 213 214 /** 215 * Asyncrhonously request the latest presence for a given contact. 216 * The result will be saved to DB directly if it can be found in DB. And then send intent 217 * com.android.ims.presence.AVAILABILITY_STATE_CHANGED to notify it. 218 * @param contactNumber the contact which will request available. 219 * Currently only support phone number. 220 * @param listener the listener to get the response. 221 * @return the resultCode which is defined by ResultCode. 222 * @note framework uses only. 223 * @hide 224 */ 225 public int requestAvailability(String contactNumber, IRcsPresenceListener listener){ 226 if(mSubscriber == null){ 227 logger.error("requestAvailability, mPresenceSubscriber is null"); 228 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE; 229 } 230 231 // check availability cache (in RAM). 232 return mSubscriber.requestAvailability(contactNumber, listener, false); 233 } 234 235 /** 236 * Same as requestAvailability. but requestAvailability will consider throttle to avoid too 237 * fast call. Which means it will not send the request to network in next 60s for the same 238 * request. 239 * The error code SUBSCRIBE_TOO_FREQUENTLY will be returned under the case. 240 * But for this funcation it will always send the request to network. 241 * 242 * @see IRcsPresenceListener 243 * @see RcsManager.ResultCode 244 * @see ResultCode.SUBSCRIBE_TOO_FREQUENTLY 245 */ 246 public int requestAvailabilityNoThrottle(String contactNumber, 247 IRcsPresenceListener listener) { 248 if(mSubscriber == null){ 249 logger.error("requestAvailabilityNoThrottle, mPresenceSubscriber is null"); 250 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE; 251 } 252 253 // check availability cache (in RAM). 254 return mSubscriber.requestAvailability(contactNumber, listener, true); 255 } 256 257 public int getPublishState() throws RemoteException { 258 return mPublication.getPublishState(); 259 } 260 }; 261 262 @Override onBind(Intent arg0)263 public IBinder onBind(Intent arg0) { 264 return mBinder; 265 } 266 267 /** 268 * Receives notifications when Mobile data is enabled or disabled. 269 */ 270 private class MobileDataContentObserver extends ContentObserver { MobileDataContentObserver()271 public MobileDataContentObserver() { 272 super(new Handler()); 273 } 274 275 @Override onChange(final boolean selfChange)276 public void onChange(final boolean selfChange) { 277 boolean enabled = Settings.Global.getInt(getContentResolver(), 278 Settings.Global.MOBILE_DATA, 1) == 1; 279 logger.debug("Mobile data enabled status: " + (enabled ? "ON" : "OFF")); 280 onMobileDataEnabled(enabled); 281 } 282 } 283 284 /** Observer to get notified when Mobile data enabled status changes */ 285 private MobileDataContentObserver mObserver; 286 onMobileDataEnabled(final boolean enabled)287 private void onMobileDataEnabled(final boolean enabled) { 288 logger.debug("Enter onMobileDataEnabled: " + enabled); 289 Thread thread = new Thread(new Runnable() { 290 @Override 291 public void run() { 292 try{ 293 if(mPublication != null){ 294 mPublication.onMobileDataChanged(enabled); 295 return; 296 } 297 }catch(Exception e){ 298 logger.error("Exception onMobileDataEnabled:", e); 299 } 300 } 301 }, "onMobileDataEnabled thread"); 302 303 thread.start(); 304 } 305 306 307 private VtSettingContentObserver mVtSettingObserver; 308 309 /** 310 * Receives notifications when Mobile data is enabled or disabled. 311 */ 312 private class VtSettingContentObserver extends ContentObserver { VtSettingContentObserver()313 public VtSettingContentObserver() { 314 super(new Handler()); 315 } 316 317 @Override onChange(final boolean selfChange)318 public void onChange(final boolean selfChange) { 319 boolean enabled = Settings.Global.getInt(getContentResolver(), 320 Settings.Global.VT_IMS_ENABLED, 1) == 1; 321 logger.debug("vt enabled status: " + (enabled ? "ON" : "OFF")); 322 323 onVtEnabled(enabled); 324 } 325 } 326 onVtEnabled(boolean enabled)327 private void onVtEnabled(boolean enabled) { 328 if(mPublication != null){ 329 mPublication.onVtEnabled(enabled); 330 } 331 } 332 333 private final IRcsService.Stub mBinder = new IRcsService.Stub() { 334 /** 335 * return true if the rcs service is ready for use. 336 */ 337 public boolean isRcsServiceAvailable(){ 338 logger.debug("calling isRcsServiceAvailable"); 339 if(mRcsStackAdaptor == null){ 340 return false; 341 } 342 343 return mRcsStackAdaptor.isImsEnableState(); 344 } 345 346 /** 347 * Gets the presence interface. 348 * 349 * @see IRcsPresence 350 */ 351 public IRcsPresence getRcsPresenceInterface(){ 352 return mIRcsPresenceImpl; 353 } 354 }; 355 registerImsConnectionStateListener()356 void registerImsConnectionStateListener() { 357 try { 358 ImsManager imsManager = ImsManager.getInstance(this, 359 SubscriptionManager.getDefaultVoicePhoneId()); 360 if (imsManager != null) { 361 imsManager.addRegistrationListener(ImsServiceClass.MMTEL, 362 mImsConnectionStateListener); 363 } 364 } catch (ImsException e) { 365 logger.error("addRegistrationListener exception=", e); 366 } 367 } 368 369 private ImsConnectionStateListener mImsConnectionStateListener = 370 new ImsConnectionStateListener() { 371 @Override 372 public void onImsConnected(int imsRadioTech) { 373 logger.debug("onImsConnected imsRadioTech=" + imsRadioTech); 374 if(mRcsStackAdaptor != null) { 375 mRcsStackAdaptor.checkSubService(); 376 } 377 378 if(mPublication != null) { 379 mPublication.onImsConnected(); 380 } 381 } 382 383 @Override 384 public void onImsDisconnected(ImsReasonInfo imsReasonInfo) { 385 logger.debug("onImsDisconnected"); 386 if(mPublication != null) { 387 mPublication.onImsDisconnected(); 388 } 389 } 390 391 @Override 392 public void onFeatureCapabilityChanged(final int serviceClass, 393 final int[] enabledFeatures, final int[] disabledFeatures) { 394 logger.debug("onFeatureCapabilityChanged"); 395 if(mPublication != null) { 396 mPublication.onFeatureCapabilityChanged(serviceClass, enabledFeatures, disabledFeatures); 397 } 398 } 399 }; 400 } 401 402