• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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