RcsService: Request ImsManager's instance with correct phoneId and serviceClass
[android/platform/frameworks/opt/net/ims.git] / rcs / rcsservice / src / com / android / service / ims / RcsService.java
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     final static String ACTION_IMS_FEATURE_STATUS_CHANGED =
76             "com.android.service.ims.presence.ims-feature-changed";
77     private static final int INVALID_SERVICE_ID = -1;
78     int mServiceId = INVALID_SERVICE_ID;
79
80     private RcsStackAdaptor mRcsStackAdaptor = null;
81     private PresencePublication mPublication = null;
82     private PresenceSubscriber mSubscriber = null;
83
84     private BroadcastReceiver mReceiver = null;
85
86     @Override
87     public void onCreate() {
88         super.onCreate();
89
90         logger.debug("RcsService onCreate");
91
92         mRcsStackAdaptor = RcsStackAdaptor.getInstance(this);
93
94         mPublication = new PresencePublication(mRcsStackAdaptor, this);
95         mRcsStackAdaptor.getListener().setPresencePublication(mPublication);
96
97         mSubscriber = new PresenceSubscriber(mRcsStackAdaptor, this);
98         mRcsStackAdaptor.getListener().setPresenceSubscriber(mSubscriber);
99         mPublication.setSubscriber(mSubscriber);
100
101         ConnectivityManager cm = ConnectivityManager.from(this);
102         if (cm != null) {
103             boolean enabled = Settings.Global.getInt(getContentResolver(),
104                     Settings.Global.MOBILE_DATA, 1) == 1;
105             logger.debug("Mobile data enabled status: " + (enabled ? "ON" : "OFF"));
106
107             onMobileDataEnabled(enabled);
108         }
109
110         // TODO: support MSIM
111         ServiceManager.addService("rcs", mBinder);
112
113         mObserver = new MobileDataContentObserver();
114         getContentResolver().registerContentObserver(
115                 Settings.Global.getUriFor(Settings.Global.MOBILE_DATA),
116                 false, mObserver);
117
118         mVtSettingObserver = new VtSettingContentObserver();
119         getContentResolver().registerContentObserver(
120                 Settings.Global.getUriFor(Settings.Global.VT_IMS_ENABLED),
121                 false, mVtSettingObserver);
122
123         registerImsConnectionStateListener();
124
125         mReceiver = new BroadcastReceiver() {
126             @Override
127             public void onReceive(Context context, Intent intent) {
128                 logger.print("onReceive intent=" + intent);
129                 if(ImsManager.ACTION_IMS_SERVICE_UP.equalsIgnoreCase(
130                         intent.getAction())){
131                     handleImsServiceUp();
132                 } else if(ImsManager.ACTION_IMS_SERVICE_DOWN.equalsIgnoreCase(
133                         intent.getAction())){
134                     handleImsServiceDown();
135                 }
136             }
137         };
138
139         IntentFilter statusFilter = new IntentFilter();
140         statusFilter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
141         statusFilter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
142         registerReceiver(mReceiver, statusFilter);
143     }
144
145     public void handleImsServiceUp() {
146         // Don't check mServiceId since it wasn't reset to INVALID_SERVICE_ID when
147         // got ACTION_IMS_SERVICE_DOWN
148         // This is phone crash case. Reset mServiceId to INVALID_SERVICE_ID
149         mServiceId = INVALID_SERVICE_ID;
150         if(mPublication != null) {
151             mPublication.handleImsServiceUp();
152         }
153
154         registerImsConnectionStateListener();
155     }
156
157     public void handleImsServiceDown() {
158         // Don't close since it could close the wrong one when phone crashed and restarted.
159         //if((mImsManager != null) && (mServiceId != INVALID_SERVICE_ID)) {
160         //    mImsManager.close(mServiceId);
161         //    mServiceId = INVALID_SERVICE_ID;
162         //}
163         if(mPublication != null) {
164             mPublication.handleImsServiceDown();
165         }
166     }
167
168
169     @Override
170     public int onStartCommand(Intent intent, int flags, int startId) {
171         logger.debug("RcsService onStartCommand");
172
173         return super.onStartCommand(intent, flags, startId);
174     }
175
176     /**
177       * Cleans up when the service is destroyed
178       */
179     @Override
180     public void onDestroy() {
181         getContentResolver().unregisterContentObserver(mObserver);
182         getContentResolver().unregisterContentObserver(mVtSettingObserver);
183         if (mReceiver != null) {
184             unregisterReceiver(mReceiver);
185             mReceiver = null;
186         }
187
188         mRcsStackAdaptor.finish();
189         mPublication.finish();
190         mPublication = null;
191         mSubscriber = null;
192
193         logger.debug("RcsService onDestroy");
194         super.onDestroy();
195     }
196
197     public PresencePublication getPublication() {
198         return mPublication;
199     }
200
201     public PresenceSubscriber getPresenceSubscriber(){
202         return mSubscriber;
203     }
204
205     IRcsPresence.Stub mIRcsPresenceImpl = new IRcsPresence.Stub(){
206         /**
207          * Asyncrhonously request the latest capability for a given contact list.
208          * The result will be saved to DB directly if the contactNumber can be found in DB.
209          * And then send intent com.android.ims.presence.CAPABILITY_STATE_CHANGED to notify it.
210          * @param contactsNumber the contact list which will request capability.
211          *                       Currently only support phone number.
212          * @param listener the listener to get the response.
213          * @return the resultCode which is defined by ResultCode.
214          * @note framework uses only.
215          * @hide
216          */
217         public int requestCapability(List<String> contactsNumber,
218             IRcsPresenceListener listener){
219             logger.debug("calling requestCapability");
220             if(mSubscriber == null){
221                 logger.debug("requestCapability, mPresenceSubscriber == null");
222                 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
223             }
224
225             return mSubscriber.requestCapability(contactsNumber, listener);
226          }
227
228         /**
229          * Asyncrhonously request the latest presence for a given contact.
230          * The result will be saved to DB directly if it can be found in DB. And then send intent
231          * com.android.ims.presence.AVAILABILITY_STATE_CHANGED to notify it.
232          * @param contactNumber the contact which will request available.
233          *                       Currently only support phone number.
234          * @param listener the listener to get the response.
235          * @return the resultCode which is defined by ResultCode.
236          * @note framework uses only.
237          * @hide
238          */
239         public int requestAvailability(String contactNumber, IRcsPresenceListener listener){
240             logger.debug("calling requestAvailability, contactNumber=" + contactNumber);
241             if(mSubscriber == null){
242                 logger.error("requestAvailability, mPresenceSubscriber is null");
243                 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
244             }
245
246             // check availability cache (in RAM).
247             return mSubscriber.requestAvailability(contactNumber, listener, false);
248         }
249
250         /**
251          * Same as requestAvailability. but requestAvailability will consider throttle to avoid too
252          * fast call. Which means it will not send the request to network in next 60s for the same
253          * request.
254          * The error code SUBSCRIBE_TOO_FREQUENTLY will be returned under the case.
255          * But for this funcation it will always send the request to network.
256          *
257          * @see IRcsPresenceListener
258          * @see RcsManager.ResultCode
259          * @see ResultCode.SUBSCRIBE_TOO_FREQUENTLY
260          */
261         public int requestAvailabilityNoThrottle(String contactNumber,
262                 IRcsPresenceListener listener) {
263             logger.debug("calling requestAvailabilityNoThrottle, contactNumber=" + contactNumber);
264             if(mSubscriber == null){
265                 logger.error("requestAvailabilityNoThrottle, mPresenceSubscriber is null");
266                 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
267             }
268
269             // check availability cache (in RAM).
270             return mSubscriber.requestAvailability(contactNumber, listener, true);
271         }
272
273         public int getPublishState() throws RemoteException {
274             return mPublication.getPublishState();
275         }
276     };
277
278     @Override
279     public IBinder onBind(Intent arg0) {
280         return mBinder;
281     }
282
283     /**
284      * Receives notifications when Mobile data is enabled or disabled.
285      */
286     private class MobileDataContentObserver extends ContentObserver {
287         public MobileDataContentObserver() {
288             super(new Handler());
289         }
290
291         @Override
292         public void onChange(final boolean selfChange) {
293             boolean enabled = Settings.Global.getInt(getContentResolver(),
294                     Settings.Global.MOBILE_DATA, 1) == 1;
295             logger.debug("Mobile data enabled status: " + (enabled ? "ON" : "OFF"));
296             onMobileDataEnabled(enabled);
297         }
298     }
299
300     /** Observer to get notified when Mobile data enabled status changes */
301     private MobileDataContentObserver mObserver;
302
303     private void onMobileDataEnabled(final boolean enabled) {
304         logger.debug("Enter onMobileDataEnabled: " + enabled);
305         Thread thread = new Thread(new Runnable() {
306             @Override
307             public void run() {
308                 try{
309                     if(mPublication != null){
310                         mPublication.onMobileDataChanged(enabled);
311                         return;
312                     }
313                 }catch(Exception e){
314                     logger.error("Exception onMobileDataEnabled:", e);
315                 }
316             }
317         }, "onMobileDataEnabled thread");
318
319         thread.start();
320     }
321
322
323     private VtSettingContentObserver mVtSettingObserver;
324
325     /**
326      * Receives notifications when Mobile data is enabled or disabled.
327      */
328     private class VtSettingContentObserver extends ContentObserver {
329         public VtSettingContentObserver() {
330             super(new Handler());
331         }
332
333         @Override
334         public void onChange(final boolean selfChange) {
335             boolean enabled = Settings.Global.getInt(getContentResolver(),
336                     Settings.Global.VT_IMS_ENABLED, 1) == 1;
337             logger.debug("vt enabled status: " + (enabled ? "ON" : "OFF"));
338
339             onVtEnabled(enabled);
340         }
341     }
342
343     private void onVtEnabled(boolean enabled) {
344         if(mPublication != null){
345             mPublication.onVtEnabled(enabled);
346         }
347     }
348
349     private final IRcsService.Stub mBinder = new IRcsService.Stub() {
350         /**
351          * return true if the rcs service is ready for use.
352          */
353         public boolean isRcsServiceAvailable(){
354             logger.debug("calling isRcsServiceAvailable");
355             if(mRcsStackAdaptor == null){
356                 return false;
357             }
358
359             return mRcsStackAdaptor.isImsEnableState();
360         }
361
362         /**
363          * Gets the presence interface.
364          *
365          * @see IRcsPresence
366          */
367         public IRcsPresence getRcsPresenceInterface(){
368             return mIRcsPresenceImpl;
369         }
370     };
371
372     void registerImsConnectionStateListener() {
373         final Context context = this;
374         Thread t = new Thread() {
375             @Override
376             public void run() {
377                 while (mServiceId == INVALID_SERVICE_ID) {
378                     try {
379                         ImsManager imsManager = ImsManager.getInstance(context,
380                                 SubscriptionManager.getDefaultVoicePhoneId());
381                         if (imsManager != null) {
382                             mServiceId = imsManager.open(ImsServiceClass.RCS,
383                                     createIncomingCallPendingIntent(),
384                                     mImsConnectionStateListener);
385                         }
386                     } catch (ImsException e) {
387                         logger.error("register exception=", e);
388                     }
389
390                     if (mServiceId == INVALID_SERVICE_ID) {
391                         try {
392                             logger.print("register wait for imsservice");
393                             sleep(300);
394                         } catch (InterruptedException e) {
395                             logger.error("register exception=", e);
396                         }
397                     } else {
398                         logger.print("register imsservice ready mServiceId="+mServiceId);
399                     }
400                 }
401             }
402         };
403
404         t.start();
405     }
406
407     private PendingIntent createIncomingCallPendingIntent() {
408         Intent intent = new Intent(ACTION_IMS_FEATURE_STATUS_CHANGED);
409         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
410         return PendingIntent.getBroadcast(this, 0, intent,
411                 PendingIntent.FLAG_UPDATE_CURRENT);
412     }
413
414     private ImsConnectionStateListener mImsConnectionStateListener =
415         new ImsConnectionStateListener() {
416             @Override
417             public void onImsConnected() {
418                 logger.debug("onImsConnected");
419                 if(mRcsStackAdaptor != null) {
420                     mRcsStackAdaptor.checkSubService();
421                 }
422
423                 if(mPublication != null) {
424                     mPublication.onImsConnected();
425                 }
426             }
427
428             @Override
429             public void onImsDisconnected(ImsReasonInfo imsReasonInfo) {
430                 logger.debug("onImsDisconnected");
431                 if(mPublication != null) {
432                     mPublication.onImsDisconnected();
433                 }
434             }
435
436             @Override
437             public void onFeatureCapabilityChanged(final int serviceClass,
438                     final int[] enabledFeatures, final int[] disabledFeatures) {
439                 logger.debug("onFeatureCapabilityChanged");
440                 if(mPublication != null) {
441                     mPublication.onFeatureCapabilityChanged(serviceClass, enabledFeatures, disabledFeatures);
442                 }
443             }
444         };
445 }
446