Home | History | Annotate | Download | only in ims
      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      */
    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 
    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      */
    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      */
    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      */
    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 
    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      */
    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
    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