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.IBinder.DeathRecipient; 35 import android.os.RemoteException; 36 import android.os.ServiceManager; 37 import android.telephony.Rlog; 38 39 import com.android.ims.internal.IRcsService; 40 import com.android.ims.internal.IRcsPresence; 41 42 import java.util.HashMap; 43 44 /** 45 * Provides APIs for Rcs services, currently it supports presence only. 46 * This class is the starting point for any RCS actions. 47 * You can acquire an instance of it with {@link #getInstance getInstance()}. 48 * 49 * @hide 50 */ 51 public class RcsManager { 52 /** 53 * For accessing the RCS related service. 54 * Internal use only. 55 * 56 * @hide 57 */ 58 private static final String RCS_SERVICE = "rcs"; 59 60 /** 61 * Part of the ACTION_RCS_SERVICE_AVAILABLE and ACTION_RCS_SERVICE_UNAVAILABLE intents. 62 * A long value; the subId corresponding to the RCS service. For MSIM implementation. 63 * 64 * @see #ACTION_RCS_SERVICE_AVAILABLE 65 * @see #ACTION_RCS_SERVICE_UNAVAILABLE 66 */ 67 public static final String EXTRA_SUBID = "android:subid"; 68 69 /** 70 * Action to broadcast when RcsService is available. 71 * 72 * @see #EXTRA_SUBID 73 */ 74 public static final String ACTION_RCS_SERVICE_AVAILABLE = 75 "com.android.ims.ACTION_RCS_SERVICE_AVAILABLE"; 76 77 /** 78 * Action to broadcast when RcsService is unavailable (such as ims is not registered). 79 * 80 * @see #EXTRA_SUBID 81 */ 82 public static final String ACTION_RCS_SERVICE_UNAVAILABLE = 83 "com.android.ims.ACTION_RCS_SERVICE_UNAVAILABLE"; 84 85 /** 86 * Action to broadcast when RcsService is died. 87 * The caller can listen to the intent to clean the pending request. 88 * 89 * It takes the extra parameter subid as well since it depends on OEM implementation for 90 * RcsService. It will not send broadcast for ACTION_RCS_SERVICE_UNAVAILABLE under the case. 91 * 92 * @see #EXTRA_SUBID 93 */ 94 public static final String ACTION_RCS_SERVICE_DIED = 95 "com.android.ims.ACTION_RCS_SERVICE_DIED"; 96 97 public static class ResultCode { 98 /** 99 * The code is used when the request is success. 100 */ 101 public static final int SUCCESS =0; 102 103 /** 104 * Return this code if the service doesn't be enabled on the phone. 105 * As per the requirement the feature can be enabled/disabled by DM. 106 */ 107 public static final int ERROR_SERVICE_NOT_ENABLED = -1; 108 109 /** 110 * Return this code if the service didn't publish yet. 111 */ 112 public static final int ERROR_SERVICE_NOT_PUBLISHED = -2; 113 114 /** 115 * The service is not available, for example it is 1x only 116 */ 117 public static final int ERROR_SERVICE_NOT_AVAILABLE = -3; 118 119 /** 120 * SUBSCRIBE Error base 121 */ 122 public static final int SUBSCRIBER_ERROR_CODE_START = ERROR_SERVICE_NOT_AVAILABLE; 123 124 /** 125 * Temporary error and need retry later. 126 * such as: 127 * 503 Service Unavailable 128 * Device shall retry with exponential back-off 129 * 130 * 408 Request Timeout 131 * Device shall retry with exponential back-off 132 * 133 * 423 Interval Too Short. Requested expiry interval too short and server rejects it 134 * Device shall re-attempt subscription after changing the expiration interval in 135 * the Expires header field to be equal to or greater than the expiration interval 136 * within the Min-Expires header field of the 423 response 137 */ 138 public static final int SUBSCRIBE_TEMPORARY_ERROR = SUBSCRIBER_ERROR_CODE_START - 1; 139 140 /** 141 * receives 403 (reason="User Not Registered"). 142 * Re-Register to IMS then retry the single resource subscription if capability polling. 143 * availability fetch: no retry. 144 */ 145 public static final int SUBSCRIBE_NOT_REGISTERED = SUBSCRIBER_ERROR_CODE_START - 2; 146 147 /** 148 * Responding for 403 - not authorized (Requestor) 149 * No retry. 150 */ 151 public static final int SUBSCRIBE_NOT_AUTHORIZED_FOR_PRESENCE = 152 SUBSCRIBER_ERROR_CODE_START - 3; 153 154 /** 155 * Responding for "403 Forbidden" or "403" 156 * Handle it as same as 404 Not found. 157 * No retry. 158 */ 159 public static final int SUBSCRIBE_FORBIDDEN = SUBSCRIBER_ERROR_CODE_START - 4; 160 161 /** 162 * Responding for 404 (target number) 163 * No retry. 164 */ 165 public static final int SUBSCRIBE_NOT_FOUND = SUBSCRIBER_ERROR_CODE_START - 5; 166 167 /** 168 * Responding for 413 - Too Large. Top app need shrink the size 169 * of request contact list and resend the request 170 */ 171 public static final int SUBSCRIBE_TOO_LARGE = SUBSCRIBER_ERROR_CODE_START - 6; 172 173 /** 174 * All subscribe errors not covered by specific errors 175 * Other 4xx/5xx/6xx 176 * 177 * Device shall not retry 178 */ 179 public static final int SUBSCRIBE_GENIRIC_FAILURE = SUBSCRIBER_ERROR_CODE_START - 7; 180 181 /** 182 * Invalid parameter - The caller should check the parameter. 183 */ 184 public static final int SUBSCRIBE_INVALID_PARAM = SUBSCRIBER_ERROR_CODE_START - 8; 185 186 /** 187 * Fetch error - The RCS statck failed to fetch the presence information. 188 */ 189 public static final int SUBSCRIBE_FETCH_ERROR = SUBSCRIBER_ERROR_CODE_START - 9; 190 191 /** 192 * Request timeout - The RCS statck returns timeout error. 193 */ 194 public static final int SUBSCRIBE_REQUEST_TIMEOUT = SUBSCRIBER_ERROR_CODE_START - 10; 195 196 /** 197 * Insufficient memory - The RCS statck returns the insufficient memory error. 198 */ 199 public static final int SUBSCRIBE_INSUFFICIENT_MEMORY = SUBSCRIBER_ERROR_CODE_START - 11; 200 201 /** 202 * Lost network error - The RCS statck returns the lost network error. 203 */ 204 public static final int SUBSCRIBE_LOST_NETWORK = SUBSCRIBER_ERROR_CODE_START - 12; 205 206 /** 207 * Not supported error - The RCS statck returns the not supported error. 208 */ 209 public static final int SUBSCRIBE_NOT_SUPPORTED = SUBSCRIBER_ERROR_CODE_START - 13; 210 211 /** 212 * Generic error - RCS Presence stack returns generic error 213 */ 214 public static final int SUBSCRIBE_GENERIC = SUBSCRIBER_ERROR_CODE_START - 14; 215 216 /** 217 * There is a request for the same number in queue. 218 */ 219 public static final int SUBSCRIBE_ALREADY_IN_QUEUE = SUBSCRIBER_ERROR_CODE_START - 16; 220 221 /** 222 * Request too frequently. 223 */ 224 public static final int SUBSCRIBE_TOO_FREQUENTLY = SUBSCRIBER_ERROR_CODE_START - 17; 225 226 /** 227 * The last Subscriber error code 228 */ 229 public static final int SUBSCRIBER_ERROR_CODE_END = SUBSCRIBER_ERROR_CODE_START - 17; 230 }; 231 232 private static final String TAG = "RcsManager"; 233 private static final boolean DBG = true; 234 235 private static HashMap<Integer, RcsManager> sRcsManagerInstances = 236 new HashMap<Integer, RcsManager>(); 237 238 private Context mContext; 239 private int mSubId; 240 private IRcsService mRcsService = null; 241 private RcsServiceDeathRecipient mDeathRecipient = new RcsServiceDeathRecipient(); 242 243 // Interface for presence 244 // TODO: Could add other RCS service such RcsChat, RcsFt later. 245 private RcsPresence mRcsPresence = null; 246 247 /** 248 * Gets a manager instance. 249 * 250 * @param context application context for creating the manager object 251 * @param subId the subscription ID for the RCS Service 252 * @return the manager instance corresponding to the subId 253 */ getInstance(Context context, int subId)254 public static RcsManager getInstance(Context context, int subId) { 255 synchronized (sRcsManagerInstances) { 256 if (sRcsManagerInstances.containsKey(subId)){ 257 return sRcsManagerInstances.get(subId); 258 } 259 260 RcsManager mgr = new RcsManager(context, subId); 261 sRcsManagerInstances.put(subId, mgr); 262 263 return mgr; 264 } 265 } 266 RcsManager(Context context, int subId)267 private RcsManager(Context context, int subId) { 268 mContext = context; 269 mSubId = subId; 270 createRcsService(true); 271 } 272 273 /** 274 * return true if the rcs service is ready for use. 275 */ isRcsServiceAvailable()276 public boolean isRcsServiceAvailable() { 277 if (DBG) Rlog.d(TAG, "call isRcsServiceAvailable ..."); 278 279 boolean ret = false; 280 281 try { 282 checkAndThrowExceptionIfServiceUnavailable(); 283 284 ret = mRcsService.isRcsServiceAvailable(); 285 } catch (RemoteException e) { 286 // return false under the case. 287 Rlog.e(TAG, "isRcsServiceAvailable RemoteException", e); 288 }catch (RcsException e){ 289 // return false under the case. 290 Rlog.e(TAG, "isRcsServiceAvailable RcsException", e); 291 } 292 293 if (DBG) Rlog.d(TAG, "isRcsServiceAvailable ret =" + ret); 294 return ret; 295 } 296 297 /** 298 * Gets the presence interface 299 * 300 * @return the RcsPresence instance. 301 * @throws if getting the RcsPresence interface results in an error. 302 */ getRcsPresenceInterface()303 public RcsPresence getRcsPresenceInterface() throws RcsException { 304 305 if (mRcsPresence == null) { 306 checkAndThrowExceptionIfServiceUnavailable(); 307 308 try { 309 IRcsPresence rcsPresence = mRcsService.getRcsPresenceInterface(); 310 if (rcsPresence == null) { 311 throw new RcsException("getRcsPresenceInterface()", 312 ResultCode.ERROR_SERVICE_NOT_AVAILABLE); 313 } 314 mRcsPresence = new RcsPresence(rcsPresence); 315 } catch (RemoteException e) { 316 throw new RcsException("getRcsPresenceInterface()", e, 317 ResultCode.ERROR_SERVICE_NOT_AVAILABLE); 318 } 319 } 320 if (DBG) Rlog.d(TAG, "getRcsPresenceInterface(), mRcsPresence= " + mRcsPresence); 321 return mRcsPresence; 322 } 323 324 /** 325 * Binds the RCS service only if the service is not created. 326 */ checkAndThrowExceptionIfServiceUnavailable()327 private void checkAndThrowExceptionIfServiceUnavailable() 328 throws RcsException { 329 if (mRcsService == null) { 330 createRcsService(true); 331 332 if (mRcsService == null) { 333 throw new RcsException("Service is unavailable", 334 ResultCode.ERROR_SERVICE_NOT_AVAILABLE); 335 } 336 } 337 } 338 getRcsServiceName(int subId)339 private static String getRcsServiceName(int subId) { 340 // use the same mechanism as IMS_SERVICE? 341 return RCS_SERVICE; 342 } 343 344 /** 345 * Binds the RCS service. 346 */ createRcsService(boolean checkService)347 private void createRcsService(boolean checkService) { 348 if (checkService) { 349 IBinder binder = ServiceManager.checkService(getRcsServiceName(mSubId)); 350 351 if (binder == null) { 352 return; 353 } 354 } 355 356 IBinder b = ServiceManager.getService(getRcsServiceName(mSubId)); 357 358 if (b != null) { 359 try { 360 b.linkToDeath(mDeathRecipient, 0); 361 } catch (RemoteException e) { 362 } 363 } 364 365 mRcsService = IRcsService.Stub.asInterface(b); 366 } 367 368 /** 369 * Death recipient class for monitoring RCS service. 370 */ 371 private class RcsServiceDeathRecipient implements IBinder.DeathRecipient { 372 @Override binderDied()373 public void binderDied() { 374 mRcsService = null; 375 mRcsPresence = null; 376 377 if (mContext != null) { 378 Intent intent = new Intent(ACTION_RCS_SERVICE_DIED); 379 intent.putExtra(EXTRA_SUBID, mSubId); 380 mContext.sendBroadcast(new Intent(intent)); 381 } 382 } 383 } 384 } 385