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.ims;
30 
31 import android.content.Context;
32 import android.content.Intent;
33 import android.os.IBinder;
34 import android.os.RemoteException;
35 import android.os.ServiceManager;
36 import android.telephony.Rlog;
37 
38 import com.android.ims.internal.IRcsService;
39 import com.android.ims.internal.IRcsPresence;
40 
41 import java.util.HashMap;
42 
43 /**
44  * Provides APIs for Rcs services, currently it supports presence only.
45  * This class is the starting point for any RCS actions.
46  * You can acquire an instance of it with {@link #getInstance getInstance()}.
47  *
48  * @hide
49  */
50 public class RcsManager {
51     /**
52      * For accessing the RCS related service.
53      * Internal use only.
54      *
55      * @hide
56      */
57     private static final String RCS_SERVICE = "rcs";
58 
59     /**
60      * Part of the ACTION_RCS_SERVICE_AVAILABLE and ACTION_RCS_SERVICE_UNAVAILABLE intents.
61      * A long value; the subId corresponding to the RCS service. For MSIM implementation.
62      *
63      * @see #ACTION_RCS_SERVICE_AVAILABLE
64      * @see #ACTION_RCS_SERVICE_UNAVAILABLE
65      */
66     public static final String EXTRA_SUBID = "android:subid";
67 
68     /**
69      * Action to broadcast when RcsService is available.
70      *
71      * @see #EXTRA_SUBID
72      */
73     public static final String ACTION_RCS_SERVICE_AVAILABLE =
74             "com.android.ims.ACTION_RCS_SERVICE_AVAILABLE";
75 
76     /**
77      * Action to broadcast when RcsService is unavailable (such as ims is not registered).
78      *
79      * @see #EXTRA_SUBID
80      */
81     public static final String ACTION_RCS_SERVICE_UNAVAILABLE =
82             "com.android.ims.ACTION_RCS_SERVICE_UNAVAILABLE";
83 
84     /**
85      * Action to broadcast when RcsService is died.
86      * The caller can listen to the intent to clean the pending request.
87      *
88      * It takes the extra parameter subid as well since it depends on OEM implementation for
89      * RcsService. It will not send broadcast for ACTION_RCS_SERVICE_UNAVAILABLE under the case.
90      *
91      * @see #EXTRA_SUBID
92      */
93     public static final String ACTION_RCS_SERVICE_DIED =
94             "com.android.ims.ACTION_RCS_SERVICE_DIED";
95 
96     ;
97 
98     private static final String TAG = "RcsManager";
99     private static final boolean DBG = true;
100 
101     private static HashMap<Integer, RcsManager> sRcsManagerInstances =
102             new HashMap<Integer, RcsManager>();
103 
104     private Context mContext;
105     private int mSubId;
106     private IRcsService mRcsService = null;
107     private RcsServiceDeathRecipient mDeathRecipient = new RcsServiceDeathRecipient();
108 
109     // Interface for presence
110     // TODO: Could add other RCS service such RcsChat, RcsFt later.
111     private RcsPresence  mRcsPresence = null;
112 
113     /**
114      * Gets a manager instance.
115      *
116      * @param context application context for creating the manager object
117      * @param subId the subscription ID for the RCS Service
118      * @return the manager instance corresponding to the subId
119      */
getInstance(Context context, int subId)120     public static RcsManager getInstance(Context context, int subId) {
121         synchronized (sRcsManagerInstances) {
122             if (sRcsManagerInstances.containsKey(subId)){
123                 return sRcsManagerInstances.get(subId);
124             }
125 
126             RcsManager mgr = new RcsManager(context, subId);
127             sRcsManagerInstances.put(subId, mgr);
128 
129             return mgr;
130         }
131     }
132 
RcsManager(Context context, int subId)133     private RcsManager(Context context, int subId) {
134         mContext = context;
135         mSubId = subId;
136         createRcsService(true);
137     }
138 
139     /**
140      * return true if the rcs service is ready for use.
141      */
isRcsServiceAvailable()142     public boolean isRcsServiceAvailable() {
143         if (DBG) Rlog.d(TAG, "call isRcsServiceAvailable ...");
144 
145         boolean ret = false;
146 
147         try {
148             checkAndThrowExceptionIfServiceUnavailable();
149 
150             ret = mRcsService.isRcsServiceAvailable();
151         }  catch (RemoteException e) {
152             // return false under the case.
153             Rlog.e(TAG, "isRcsServiceAvailable RemoteException", e);
154         }catch (RcsException e){
155             // return false under the case.
156             Rlog.e(TAG, "isRcsServiceAvailable RcsException", e);
157         }
158 
159         if (DBG) Rlog.d(TAG, "isRcsServiceAvailable ret =" + ret);
160         return ret;
161     }
162 
163     /**
164      * Gets the presence interface
165      *
166      * @return the RcsPresence instance.
167      * @throws  if getting the RcsPresence interface results in an error.
168      */
getRcsPresenceInterface()169     public RcsPresence getRcsPresenceInterface() throws RcsException {
170 
171         if (mRcsPresence == null) {
172             checkAndThrowExceptionIfServiceUnavailable();
173 
174             try {
175                 IRcsPresence rcsPresence = mRcsService.getRcsPresenceInterface();
176                 if (rcsPresence == null) {
177                     throw new RcsException("getRcsPresenceInterface()",
178                             ResultCode.ERROR_SERVICE_NOT_AVAILABLE);
179                 }
180                 mRcsPresence = new RcsPresence(rcsPresence);
181             } catch (RemoteException e) {
182                 throw new RcsException("getRcsPresenceInterface()", e,
183                         ResultCode.ERROR_SERVICE_NOT_AVAILABLE);
184             }
185         }
186         if (DBG) Rlog.d(TAG, "getRcsPresenceInterface(), mRcsPresence= " + mRcsPresence);
187         return mRcsPresence;
188     }
189 
190     /**
191      * Binds the RCS service only if the service is not created.
192      */
checkAndThrowExceptionIfServiceUnavailable()193     private void checkAndThrowExceptionIfServiceUnavailable()
194             throws  RcsException {
195         if (mRcsService == null) {
196             createRcsService(true);
197 
198             if (mRcsService == null) {
199                 throw new RcsException("Service is unavailable",
200                         ResultCode.ERROR_SERVICE_NOT_AVAILABLE);
201             }
202         }
203     }
204 
getRcsServiceName(int subId)205     private static String getRcsServiceName(int subId) {
206         // use the same mechanism as IMS_SERVICE?
207         return RCS_SERVICE;
208     }
209 
210     /**
211      * Binds the RCS service.
212      */
createRcsService(boolean checkService)213     private void createRcsService(boolean checkService) {
214         if (checkService) {
215             IBinder binder = ServiceManager.checkService(getRcsServiceName(mSubId));
216 
217             if (binder == null) {
218                 return;
219             }
220         }
221 
222         IBinder b = ServiceManager.getService(getRcsServiceName(mSubId));
223 
224         if (b != null) {
225             try {
226                 b.linkToDeath(mDeathRecipient, 0);
227             } catch (RemoteException e) {
228             }
229         }
230 
231         mRcsService = IRcsService.Stub.asInterface(b);
232     }
233 
234     /**
235      * Death recipient class for monitoring RCS service.
236      */
237     private class RcsServiceDeathRecipient implements IBinder.DeathRecipient {
238         @Override
binderDied()239         public void binderDied() {
240             mRcsService = null;
241             mRcsPresence = null;
242 
243             if (mContext != null) {
244                 Intent intent = new Intent(ACTION_RCS_SERVICE_DIED);
245                 intent.putExtra(EXTRA_SUBID, mSubId);
246                 mContext.sendBroadcast(new Intent(intent));
247             }
248         }
249     }
250 }
251