blob: c198bb309ab1560c81fffe1a9b4300a8e611dc1b [file] [log] [blame]
/*
* Copyright (c) 2015, Motorola Mobility LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - Neither the name of Motorola Mobility nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package com.android.ims;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.Rlog;
import com.android.ims.internal.IRcsService;
import com.android.ims.internal.IRcsPresence;
import java.util.HashMap;
/**
* Provides APIs for Rcs services, currently it supports presence only.
* This class is the starting point for any RCS actions.
* You can acquire an instance of it with {@link #getInstance getInstance()}.
*
* @hide
*/
public class RcsManager {
/**
* For accessing the RCS related service.
* Internal use only.
*
* @hide
*/
private static final String RCS_SERVICE = "rcs";
/**
* Part of the ACTION_RCS_SERVICE_AVAILABLE and ACTION_RCS_SERVICE_UNAVAILABLE intents.
* A long value; the subId corresponding to the RCS service. For MSIM implementation.
*
* @see #ACTION_RCS_SERVICE_AVAILABLE
* @see #ACTION_RCS_SERVICE_UNAVAILABLE
*/
public static final String EXTRA_SUBID = "android:subid";
/**
* Action to broadcast when RcsService is available.
*
* @see #EXTRA_SUBID
*/
public static final String ACTION_RCS_SERVICE_AVAILABLE =
"com.android.ims.ACTION_RCS_SERVICE_AVAILABLE";
/**
* Action to broadcast when RcsService is unavailable (such as ims is not registered).
*
* @see #EXTRA_SUBID
*/
public static final String ACTION_RCS_SERVICE_UNAVAILABLE =
"com.android.ims.ACTION_RCS_SERVICE_UNAVAILABLE";
/**
* Action to broadcast when RcsService is died.
* The caller can listen to the intent to clean the pending request.
*
* It takes the extra parameter subid as well since it depends on OEM implementation for
* RcsService. It will not send broadcast for ACTION_RCS_SERVICE_UNAVAILABLE under the case.
*
* @see #EXTRA_SUBID
*/
public static final String ACTION_RCS_SERVICE_DIED =
"com.android.ims.ACTION_RCS_SERVICE_DIED";
public static class ResultCode {
/**
* The code is used when the request is success.
*/
public static final int SUCCESS =0;
/**
* Return this code if the service doesn't be enabled on the phone.
* As per the requirement the feature can be enabled/disabled by DM.
*/
public static final int ERROR_SERVICE_NOT_ENABLED = -1;
/**
* Return this code if the service didn't publish yet.
*/
public static final int ERROR_SERVICE_NOT_PUBLISHED = -2;
/**
* The service is not available, for example it is 1x only
*/
public static final int ERROR_SERVICE_NOT_AVAILABLE = -3;
/**
* SUBSCRIBE Error base
*/
public static final int SUBSCRIBER_ERROR_CODE_START = ERROR_SERVICE_NOT_AVAILABLE;
/**
* Temporary error and need retry later.
* such as:
* 503 Service Unavailable
* Device shall retry with exponential back-off
*
* 408 Request Timeout
* Device shall retry with exponential back-off
*
* 423 Interval Too Short. Requested expiry interval too short and server rejects it
* Device shall re-attempt subscription after changing the expiration interval in
* the Expires header field to be equal to or greater than the expiration interval
* within the Min-Expires header field of the 423 response
*/
public static final int SUBSCRIBE_TEMPORARY_ERROR = SUBSCRIBER_ERROR_CODE_START - 1;
/**
* receives 403 (reason="User Not Registered").
* Re-Register to IMS then retry the single resource subscription if capability polling.
* availability fetch: no retry.
*/
public static final int SUBSCRIBE_NOT_REGISTERED = SUBSCRIBER_ERROR_CODE_START - 2;
/**
* Responding for 403 - not authorized (Requestor)
* No retry.
*/
public static final int SUBSCRIBE_NOT_AUTHORIZED_FOR_PRESENCE =
SUBSCRIBER_ERROR_CODE_START - 3;
/**
* Responding for "403 Forbidden" or "403"
* Handle it as same as 404 Not found.
* No retry.
*/
public static final int SUBSCRIBE_FORBIDDEN = SUBSCRIBER_ERROR_CODE_START - 4;
/**
* Responding for 404 (target number)
* No retry.
*/
public static final int SUBSCRIBE_NOT_FOUND = SUBSCRIBER_ERROR_CODE_START - 5;
/**
* Responding for 413 - Too Large. Top app need shrink the size
* of request contact list and resend the request
*/
public static final int SUBSCRIBE_TOO_LARGE = SUBSCRIBER_ERROR_CODE_START - 6;
/**
* All subscribe errors not covered by specific errors
* Other 4xx/5xx/6xx
*
* Device shall not retry
*/
public static final int SUBSCRIBE_GENIRIC_FAILURE = SUBSCRIBER_ERROR_CODE_START - 7;
/**
* Invalid parameter - The caller should check the parameter.
*/
public static final int SUBSCRIBE_INVALID_PARAM = SUBSCRIBER_ERROR_CODE_START - 8;
/**
* Fetch error - The RCS statck failed to fetch the presence information.
*/
public static final int SUBSCRIBE_FETCH_ERROR = SUBSCRIBER_ERROR_CODE_START - 9;
/**
* Request timeout - The RCS statck returns timeout error.
*/
public static final int SUBSCRIBE_REQUEST_TIMEOUT = SUBSCRIBER_ERROR_CODE_START - 10;
/**
* Insufficient memory - The RCS statck returns the insufficient memory error.
*/
public static final int SUBSCRIBE_INSUFFICIENT_MEMORY = SUBSCRIBER_ERROR_CODE_START - 11;
/**
* Lost network error - The RCS statck returns the lost network error.
*/
public static final int SUBSCRIBE_LOST_NETWORK = SUBSCRIBER_ERROR_CODE_START - 12;
/**
* Not supported error - The RCS statck returns the not supported error.
*/
public static final int SUBSCRIBE_NOT_SUPPORTED = SUBSCRIBER_ERROR_CODE_START - 13;
/**
* Generic error - RCS Presence stack returns generic error
*/
public static final int SUBSCRIBE_GENERIC = SUBSCRIBER_ERROR_CODE_START - 14;
/**
* There is a request for the same number in queue.
*/
public static final int SUBSCRIBE_ALREADY_IN_QUEUE = SUBSCRIBER_ERROR_CODE_START - 16;
/**
* Request too frequently.
*/
public static final int SUBSCRIBE_TOO_FREQUENTLY = SUBSCRIBER_ERROR_CODE_START - 17;
/**
* The last Subscriber error code
*/
public static final int SUBSCRIBER_ERROR_CODE_END = SUBSCRIBER_ERROR_CODE_START - 17;
};
private static final String TAG = "RcsManager";
private static final boolean DBG = true;
private static HashMap<Integer, RcsManager> sRcsManagerInstances =
new HashMap<Integer, RcsManager>();
private Context mContext;
private int mSubId;
private IRcsService mRcsService = null;
private RcsServiceDeathRecipient mDeathRecipient = new RcsServiceDeathRecipient();
private boolean mPresenceEnabledByFramework = true;
// Interface for presence
// TODO: Could add other RCS service such RcsChat, RcsFt later.
private RcsPresence mRcsPresence = null;
/**
* Gets a manager instance.
*
* @param context application context for creating the manager object
* @param subId the subscription ID for the RCS Service
* @return the manager instance corresponding to the subId
*/
public static RcsManager getInstance(Context context, int subId) {
synchronized (sRcsManagerInstances) {
if (sRcsManagerInstances.containsKey(subId)){
return sRcsManagerInstances.get(subId);
}
RcsManager mgr = new RcsManager(context, subId);
sRcsManagerInstances.put(subId, mgr);
return mgr;
}
}
private RcsManager(Context context, int subId) {
mContext = context;
mSubId = subId;
createRcsService(true);
}
/**
* return true if the rcs service is ready for use.
*/
public boolean isRcsServiceAvailable() {
if (DBG) Rlog.d(TAG, "call isRcsServiceAvailable ...");
boolean ret = false;
try {
checkAndThrowExceptionIfServiceUnavailable();
ret = mRcsService.isRcsServiceAvailable();
} catch (RemoteException e) {
// return false under the case.
Rlog.e(TAG, "isRcsServiceAvailable RemoteException", e);
}catch (RcsException e){
// return false under the case.
Rlog.e(TAG, "isRcsServiceAvailable RcsException", e);
}
if (DBG) Rlog.d(TAG, "isRcsServiceAvailable ret =" + ret + " mPresenceEnabledByFramework ="
+ mPresenceEnabledByFramework);
return ret && mPresenceEnabledByFramework;
}
/**
* Gets the presence interface
*
* @return the RcsPresence instance.
* @throws if getting the RcsPresence interface results in an error.
*/
public RcsPresence getRcsPresenceInterface() throws RcsException {
if (mRcsPresence == null) {
checkAndThrowExceptionIfServiceUnavailable();
try {
IRcsPresence rcsPresence = mRcsService.getRcsPresenceInterface();
if (rcsPresence == null) {
throw new RcsException("getRcsPresenceInterface()",
ResultCode.ERROR_SERVICE_NOT_AVAILABLE);
}
mRcsPresence = new RcsPresence(rcsPresence);
} catch (RemoteException e) {
throw new RcsException("getRcsPresenceInterface()", e,
ResultCode.ERROR_SERVICE_NOT_AVAILABLE);
}
}
if (DBG) Rlog.d(TAG, "getRcsPresenceInterface(), mRcsPresence= " + mRcsPresence);
return mRcsPresence;
}
/**
* Binds the RCS service only if the service is not created.
*/
private void checkAndThrowExceptionIfServiceUnavailable()
throws RcsException {
if (mRcsService == null) {
createRcsService(true);
if (mRcsService == null) {
throw new RcsException("Service is unavailable",
ResultCode.ERROR_SERVICE_NOT_AVAILABLE);
}
}
}
private static String getRcsServiceName(int subId) {
// use the same mechanism as IMS_SERVICE?
return RCS_SERVICE;
}
/**
* Binds the RCS service.
*/
private void createRcsService(boolean checkService) {
if (checkService) {
IBinder binder = ServiceManager.checkService(getRcsServiceName(mSubId));
if (binder == null) {
return;
}
}
IBinder b = ServiceManager.getService(getRcsServiceName(mSubId));
if (b != null) {
try {
b.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
}
}
mRcsService = IRcsService.Stub.asInterface(b);
}
/**
* Death recipient class for monitoring RCS service.
*/
private class RcsServiceDeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
mRcsService = null;
mRcsPresence = null;
if (mContext != null) {
Intent intent = new Intent(ACTION_RCS_SERVICE_DIED);
intent.putExtra(EXTRA_SUBID, mSubId);
mContext.sendBroadcast(new Intent(intent));
}
}
}
public void setPresenceEnabledByFramework(boolean presenceEnabledByFramework) {
mPresenceEnabledByFramework = presenceEnabledByFramework;
}
}