Merge "Modify RCS UCE APIs to use AIDL Interfaces."

am: 4204a6017e

Change-Id: I3c10e76be4266bb4f43306ca3f0c7362499208f7
This commit is contained in:
Brad Ebinger
2019-08-13 16:34:41 -07:00
committed by android-build-merger
10 changed files with 514 additions and 149 deletions

View File

@@ -560,6 +560,7 @@ java_defaults {
"telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl", "telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl",
"telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl", "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
"telephony/java/android/telephony/ims/aidl/IRcsMessage.aidl", "telephony/java/android/telephony/ims/aidl/IRcsMessage.aidl",
"telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl",
"telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl", "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl", "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl", "telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl",

View File

@@ -39,11 +39,11 @@ public final class ImsException extends Exception {
*/ */
public static final int CODE_ERROR_UNSPECIFIED = 0; public static final int CODE_ERROR_UNSPECIFIED = 0;
/** /**
* The operation has failed because there is no {@link ImsService} available to service it. This * The operation has failed because there is no remote process available to service it. This
* may be due to an {@link ImsService} crash or other illegal state. * may be due to a process crash or other illegal state.
* <p> * <p>
* This is a temporary error and the operation may be retried until the connection to the * This is a temporary error and the operation may be retried until the connection to the
* {@link ImsService} is restored. * remote process is restored.
*/ */
public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1; public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1;

View File

@@ -16,10 +16,38 @@
package android.telephony.ims.aidl; package android.telephony.ims.aidl;
import android.net.Uri;
import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IRcsFeatureListener;
import android.telephony.ims.feature.CapabilityChangeRequest;
import java.util.List;
/** /**
* See RcsFeature for more information. * See RcsFeature for more information.
* {@hide} * {@hide}
*/ */
interface IImsRcsFeature { interface IImsRcsFeature {
//Empty Default Implementation // Not oneway because we need to verify this completes before doing anything else.
void setListener(IRcsFeatureListener listener);
int queryCapabilityStatus();
// Inherited from ImsFeature
int getFeatureState();
oneway void addCapabilityCallback(IImsCapabilityCallback c);
oneway void removeCapabilityCallback(IImsCapabilityCallback c);
oneway void changeCapabilitiesConfiguration(in CapabilityChangeRequest r,
IImsCapabilityCallback c);
oneway void queryCapabilityConfiguration(int capability, int radioTech,
IImsCapabilityCallback c);
// RcsPresenceExchangeImplBase specific api
oneway void requestCapabilities(in List<Uri> uris, int operationToken);
oneway void updateCapabilities(in RcsContactUceCapability capabilities, int operationToken);
// RcsSipOptionsImplBase specific api
oneway void sendCapabilityRequest(in Uri contactUri,
in RcsContactUceCapability capabilities, int operationToken);
oneway void respondToCapabilityRequest(in String contactUri,
in RcsContactUceCapability ownCapabilities, int operationToken);
oneway void respondToCapabilityRequestWithError(in Uri contactUri, int code, in String reason,
int operationToken);
} }

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.telephony.ims.aidl;
import android.net.Uri;
import android.telephony.ims.RcsContactUceCapability;
import java.util.List;
/**
* Listener interface for updates from the RcsFeature back to the framework.
* {@hide}
*/
interface IRcsFeatureListener {
//RcsCapabilityExchange specific
oneway void onCommandUpdate(int commandCode, int operationToken);
// RcsPresenceExchangeImplBase Specific
oneway void onNetworkResponse(int code, in String reason, int operationToken);
oneway void onCapabilityRequestResponsePresence(in List<RcsContactUceCapability> infos,
int operationToken);
oneway void onNotifyUpdateCapabilities();
oneway void onUnpublish();
// RcsSipOptionsImplBase specific
oneway void onCapabilityRequestResponseOptions(int code, in String reason,
in RcsContactUceCapability info, int operationToken);
oneway void onRemoteCapabilityRequest(in Uri contactUri, in RcsContactUceCapability remoteInfo,
int operationToken);
}

View File

@@ -33,12 +33,8 @@ import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
/** /**
* Base class for all IMS features that are supported by the framework. Use a concrete subclass * Base class for all IMS features that are supported by the framework. Use a concrete subclass
@@ -51,35 +47,6 @@ public abstract class ImsFeature {
private static final String LOG_TAG = "ImsFeature"; private static final String LOG_TAG = "ImsFeature";
/**
* Action to broadcast when ImsService is up.
* Internal use only.
* Only defined here separately for compatibility purposes with the old ImsService.
*
* @hide
*/
public static final String ACTION_IMS_SERVICE_UP =
"com.android.ims.IMS_SERVICE_UP";
/**
* Action to broadcast when ImsService is down.
* Internal use only.
* Only defined here separately for compatibility purposes with the old ImsService.
*
* @hide
*/
public static final String ACTION_IMS_SERVICE_DOWN =
"com.android.ims.IMS_SERVICE_DOWN";
/**
* Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
* A long value; the phone ID corresponding to the IMS service coming up or down.
* Only defined here separately for compatibility purposes with the old ImsService.
*
* @hide
*/
public static final String EXTRA_PHONE_ID = "android:phone_id";
/** /**
* Invalid feature value * Invalid feature value
* @hide * @hide
@@ -335,8 +302,8 @@ public abstract class ImsFeature {
/** @hide */ /** @hide */
protected final Object mLock = new Object(); protected final Object mLock = new Object();
private final Set<IImsFeatureStatusCallback> mStatusCallbacks = private final RemoteCallbackList<IImsFeatureStatusCallback> mStatusCallbacks =
Collections.newSetFromMap(new WeakHashMap<>()); new RemoteCallbackList<>();
private @ImsState int mState = STATE_UNAVAILABLE; private @ImsState int mState = STATE_UNAVAILABLE;
private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks = private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks =
@@ -397,9 +364,7 @@ public abstract class ImsFeature {
// If we have just connected, send queued status. // If we have just connected, send queued status.
c.notifyImsFeatureStatus(getFeatureState()); c.notifyImsFeatureStatus(getFeatureState());
// Add the callback if the callback completes successfully without a RemoteException. // Add the callback if the callback completes successfully without a RemoteException.
synchronized (mLock) { mStatusCallbacks.register(c);
mStatusCallbacks.add(c);
}
} catch (RemoteException e) { } catch (RemoteException e) {
Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage()); Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
} }
@@ -411,29 +376,21 @@ public abstract class ImsFeature {
*/ */
@VisibleForTesting @VisibleForTesting
public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) { public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
synchronized (mLock) { mStatusCallbacks.unregister(c);
mStatusCallbacks.remove(c);
}
} }
/** /**
* Internal method called by ImsFeature when setFeatureState has changed. * Internal method called by ImsFeature when setFeatureState has changed.
*/ */
private void notifyFeatureState(@ImsState int state) { private void notifyFeatureState(@ImsState int state) {
synchronized (mLock) { mStatusCallbacks.broadcast((c) -> {
for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
iter.hasNext(); ) {
IImsFeatureStatusCallback callback = iter.next();
try { try {
Log.i(LOG_TAG, "notifying ImsFeatureState=" + state); c.notifyImsFeatureStatus(state);
callback.notifyImsFeatureStatus(state);
} catch (RemoteException e) { } catch (RemoteException e) {
// remove if the callback is no longer alive. Log.w(LOG_TAG, e + " notifyFeatureState() - Skipping "
iter.remove(); + "callback.");
Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
}
}
} }
});
} }
/** /**
@@ -452,10 +409,23 @@ public abstract class ImsFeature {
/** /**
* @hide * @hide
*/ */
public final void removeCapabilityCallback(IImsCapabilityCallback c) { final void removeCapabilityCallback(IImsCapabilityCallback c) {
mCapabilityCallbacks.unregister(c); mCapabilityCallbacks.unregister(c);
} }
/**@hide*/
final void queryCapabilityConfigurationInternal(int capability, int radioTech,
IImsCapabilityCallback c) {
boolean enabled = queryCapabilityConfiguration(capability, radioTech);
try {
if (c != null) {
c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
}
} catch (RemoteException e) {
Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
}
}
/** /**
* @return the cached capabilities status for this feature. * @return the cached capabilities status for this feature.
* @hide * @hide
@@ -484,30 +454,35 @@ public abstract class ImsFeature {
/** /**
* Called by the ImsFeature when the capabilities status has changed. * Called by the ImsFeature when the capabilities status has changed.
* *
* @param c A {@link Capabilities} containing the new Capabilities status. * @param caps the new {@link Capabilities} status of the {@link ImsFeature}.
* *
* @hide * @hide
*/ */
protected final void notifyCapabilitiesStatusChanged(Capabilities c) { protected final void notifyCapabilitiesStatusChanged(Capabilities caps) {
synchronized (mLock) { synchronized (mLock) {
mCapabilityStatus = c.copy(); mCapabilityStatus = caps.copy();
} }
int count = mCapabilityCallbacks.beginBroadcast(); mCapabilityCallbacks.broadcast((callback) -> {
try { try {
for (int i = 0; i < count; i++) { callback.onCapabilitiesStatusChanged(caps.mCapabilities);
try {
mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
c.mCapabilities);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " + Log.w(LOG_TAG, e + " notifyCapabilitiesStatusChanged() - Skipping "
"callback."); + "callback.");
}
}
} finally {
mCapabilityCallbacks.finishBroadcast();
} }
});
} }
/**
* Provides the ImsFeature with the ability to return the framework Capability Configuration
* for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
* includes a capability A to enable or disable, this method should return the correct enabled
* status for capability A.
* @param capability The capability that we are querying the configuration for.
* @return true if the capability is enabled, false otherwise.
* @hide
*/
public abstract boolean queryCapabilityConfiguration(int capability, int radioTech);
/** /**
* Features should override this method to receive Capability preference change requests from * Features should override this method to receive Capability preference change requests from
* the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities

View File

@@ -37,7 +37,6 @@ import android.telephony.ims.stub.ImsMultiEndpointImplBase;
import android.telephony.ims.stub.ImsRegistrationImplBase; import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase; import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsUtImplBase; import android.telephony.ims.stub.ImsUtImplBase;
import android.util.Log;
import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm; import com.android.ims.internal.IImsEcbm;
@@ -154,18 +153,14 @@ public class MmTelFeature extends ImsFeature {
@Override @Override
public void changeCapabilitiesConfiguration(CapabilityChangeRequest request, public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
IImsCapabilityCallback c) { IImsCapabilityCallback c) {
synchronized (mLock) {
MmTelFeature.this.requestChangeEnabledCapabilities(request, c); MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
} }
}
@Override @Override
public void queryCapabilityConfiguration(int capability, int radioTech, public void queryCapabilityConfiguration(int capability, int radioTech,
IImsCapabilityCallback c) { IImsCapabilityCallback c) {
synchronized (mLock) {
queryCapabilityConfigurationInternal(capability, radioTech, c); queryCapabilityConfigurationInternal(capability, radioTech, c);
} }
}
@Override @Override
public void setSmsListener(IImsSmsListener l) { public void setSmsListener(IImsSmsListener l) {
@@ -381,18 +376,6 @@ public class MmTelFeature extends ImsFeature {
} }
} }
private void queryCapabilityConfigurationInternal(int capability, int radioTech,
IImsCapabilityCallback c) {
boolean enabled = queryCapabilityConfiguration(capability, radioTech);
try {
if (c != null) {
c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
}
} catch (RemoteException e) {
Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
}
}
/** /**
* The current capability status that this MmTelFeature has defined is available. This * The current capability status that this MmTelFeature has defined is available. This
* configuration will be used by the platform to figure out which capabilities are CURRENTLY * configuration will be used by the platform to figure out which capabilities are CURRENTLY
@@ -512,6 +495,7 @@ public class MmTelFeature extends ImsFeature {
* @param capability The capability that we are querying the configuration for. * @param capability The capability that we are querying the configuration for.
* @return true if the capability is enabled, false otherwise. * @return true if the capability is enabled, false otherwise.
*/ */
@Override
public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability, public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
// Base implementation - Override to provide functionality // Base implementation - Override to provide functionality

View File

@@ -16,14 +16,32 @@
package android.telephony.ims.feature; package android.telephony.ims.feature;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi; import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRcsFeature; import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IRcsFeatureListener;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.RcsPresenceExchangeImplBase; import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
import android.telephony.ims.stub.RcsSipOptionsImplBase; import android.telephony.ims.stub.RcsSipOptionsImplBase;
import android.util.Log;
import com.android.internal.util.FunctionalUtils;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
/** /**
* Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
@@ -33,10 +51,132 @@ import java.lang.annotation.RetentionPolicy;
@SystemApi @SystemApi
public class RcsFeature extends ImsFeature { public class RcsFeature extends ImsFeature {
/**{@inheritDoc}*/ private static final String LOG_TAG = "RcsFeature";
private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
// Empty Default Implementation. private static final class RcsFeatureBinder extends IImsRcsFeature.Stub {
}; // Reference the outer class in order to have better test coverage metrics instead of
// creating a inner class referencing the outer class directly.
private final RcsFeature mReference;
private final Executor mExecutor;
RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) {
mReference = classRef;
mExecutor = executor;
}
@Override
public void setListener(IRcsFeatureListener listener) {
mReference.setListener(listener);
}
@Override
public int queryCapabilityStatus() throws RemoteException {
return executeMethodAsyncForResult(
() -> mReference.queryCapabilityStatus().mCapabilities,
"queryCapabilityStatus");
}
@Override
public void addCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
executeMethodAsync(() -> mReference.addCapabilityCallback(c), "addCapabilityCallback");
}
@Override
public void removeCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
executeMethodAsync(() -> mReference.removeCapabilityCallback(c),
"removeCapabilityCallback");
}
@Override
public void changeCapabilitiesConfiguration(CapabilityChangeRequest r,
IImsCapabilityCallback c) throws RemoteException {
executeMethodAsync(() -> mReference.requestChangeEnabledCapabilities(r, c),
"changeCapabilitiesConfiguration");
}
@Override
public void queryCapabilityConfiguration(int capability, int radioTech,
IImsCapabilityCallback c) throws RemoteException {
executeMethodAsync(() -> mReference.queryCapabilityConfigurationInternal(capability,
radioTech, c), "queryCapabilityConfiguration");
}
@Override
public int getFeatureState() throws RemoteException {
return executeMethodAsyncForResult(mReference::getFeatureState, "getFeatureState");
}
// RcsPresenceExchangeImplBase specific APIS
@Override
public void requestCapabilities(List<Uri> uris, int operationToken) throws RemoteException {
executeMethodAsync(() -> mReference.getPresenceExchangeInternal()
.requestCapabilities(uris, operationToken), "requestCapabilities");
}
@Override
public void updateCapabilities(RcsContactUceCapability capabilities, int operationToken)
throws RemoteException {
executeMethodAsync(() -> mReference.getPresenceExchangeInternal()
.updateCapabilities(capabilities, operationToken),
"updateCapabilities");
}
// RcsSipOptionsImplBase specific APIS
@Override
public void sendCapabilityRequest(Uri contactUri, RcsContactUceCapability capabilities,
int operationToken) throws RemoteException {
executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
.sendCapabilityRequest(contactUri, capabilities, operationToken),
"sendCapabilityRequest");
}
@Override
public void respondToCapabilityRequest(String contactUri,
RcsContactUceCapability ownCapabilities, int operationToken)
throws RemoteException {
executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
.respondToCapabilityRequest(contactUri, ownCapabilities,
operationToken), "respondToCapabilityRequest");
}
@Override
public void respondToCapabilityRequestWithError(Uri contactUri, int code, String reason,
int operationToken) throws RemoteException {
executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
.respondToCapabilityRequestWithError(contactUri, code, reason,
operationToken), "respondToCapabilityRequestWithError");
}
// Call the methods with a clean calling identity on the executor and wait indefinitely for
// the future to return.
private void executeMethodAsync(FunctionalUtils.ThrowingRunnable r, String errorLogName)
throws RemoteException {
// call with a clean calling identity on the executor and wait indefinitely for the
// future to return.
try {
CompletableFuture.runAsync(
() -> Binder.withCleanCallingIdentity(r), mExecutor).join();
} catch (CancellationException | CompletionException e) {
Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
+ e.getMessage());
throw new RemoteException(e.getMessage());
}
}
private <T> T executeMethodAsyncForResult(FunctionalUtils.ThrowingSupplier<T> r,
String errorLogName) throws RemoteException {
// call with a clean calling identity on the executor and wait indefinitely for the
// future to return.
CompletableFuture<T> future = CompletableFuture.supplyAsync(
() -> Binder.withCleanCallingIdentity(r), mExecutor);
try {
return future.get();
} catch (ExecutionException | InterruptedException e) {
Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
+ e.getMessage());
throw new RemoteException(e.getMessage());
}
}
}
/** /**
* Contains the capabilities defined and supported by a {@link RcsFeature} in the * Contains the capabilities defined and supported by a {@link RcsFeature} in the
@@ -81,27 +221,66 @@ public class RcsFeature extends ImsFeature {
/**@hide*/ /**@hide*/
public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) { public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) {
super(capabilities);
}
/**@hide*/
private RcsImsCapabilities(Capabilities c) {
super(c.getMask());
} }
/**@hide*/ /**@hide*/
@Override @Override
public void addCapabilities(@RcsImsCapabilityFlag int capabilities) { public void addCapabilities(@RcsImsCapabilityFlag int capabilities) {
super.addCapabilities(capabilities);
} }
/**@hide*/ /**@hide*/
@Override @Override
public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) { public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) {
super.removeCapabilities(capabilities);
} }
/**@hide*/ /**@hide*/
@Override @Override
public boolean isCapable(@RcsImsCapabilityFlag int capabilities) { public boolean isCapable(@RcsImsCapabilityFlag int capabilities) {
return false; return super.isCapable(capabilities);
} }
} }
private final RcsFeatureBinder mImsRcsBinder;
private IRcsFeatureListener mListenerBinder;
private RcsPresenceExchangeImplBase mPresExchange;
private RcsSipOptionsImplBase mSipOptions;
/**
* Create a new RcsFeature.
* <p>
* Method stubs called from the framework will be called asynchronously. To specify the
* {@link Executor} that the methods stubs will be called, use
* {@link RcsFeature#RcsFeature(Executor)} instead.
*/
public RcsFeature() {
super();
// Run on the Binder threads that call them.
mImsRcsBinder = new RcsFeatureBinder(this, Runnable::run);
}
/**
* Create a new RcsFeature using the Executor specified for methods being called by the
* framework.
* @param executor The executor for the framework to use when making calls to this service.
* @hide
*/
public RcsFeature(@NonNull Executor executor) {
super();
if (executor == null) {
throw new IllegalArgumentException("executor can not be null.");
}
// Run on the Binder thread by default.
mImsRcsBinder = new RcsFeatureBinder(this, executor);
}
/** /**
* Query the current {@link RcsImsCapabilities} status set by the RcsFeature. If a capability is * Query the current {@link RcsImsCapabilities} status set by the RcsFeature. If a capability is
* set, the {@link RcsFeature} has brought up the capability and is ready for framework * set, the {@link RcsFeature} has brought up the capability and is ready for framework
@@ -111,7 +290,7 @@ public class RcsFeature extends ImsFeature {
*/ */
@Override @Override
public final RcsImsCapabilities queryCapabilityStatus() { public final RcsImsCapabilities queryCapabilityStatus() {
throw new UnsupportedOperationException(); return new RcsImsCapabilities(super.queryCapabilityStatus());
} }
/** /**
@@ -120,8 +299,11 @@ public class RcsFeature extends ImsFeature {
* Call {@link #queryCapabilityStatus()} to return the current capability status. * Call {@link #queryCapabilityStatus()} to return the current capability status.
* @hide * @hide
*/ */
public final void notifyCapabilitiesStatusChanged(RcsImsCapabilities c) { public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
throw new UnsupportedOperationException(); if (c == null) {
throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
}
super.notifyCapabilitiesStatusChanged(c);
} }
/** /**
@@ -133,8 +315,10 @@ public class RcsFeature extends ImsFeature {
* @hide * @hide
*/ */
public boolean queryCapabilityConfiguration( public boolean queryCapabilityConfiguration(
@RcsImsCapabilities.RcsImsCapabilityFlag int capability) { @RcsImsCapabilities.RcsImsCapabilityFlag int capability,
throw new UnsupportedOperationException(); @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
// Base Implementation - Override to provide functionality
return false;
} }
/** /**
* Called from the framework when the {@link RcsImsCapabilities} that have been configured for * Called from the framework when the {@link RcsImsCapabilities} that have been configured for
@@ -155,7 +339,7 @@ public class RcsFeature extends ImsFeature {
@Override @Override
public void changeEnabledCapabilities(CapabilityChangeRequest request, public void changeEnabledCapabilities(CapabilityChangeRequest request,
CapabilityCallbackProxy c) { CapabilityCallbackProxy c) {
throw new UnsupportedOperationException(); // Base Implementation - Override to provide functionality
} }
/** /**
@@ -192,13 +376,6 @@ public class RcsFeature extends ImsFeature {
return new RcsPresenceExchangeImplBase(); return new RcsPresenceExchangeImplBase();
} }
/**
* Construct a new {@link RcsFeature} instance.
*/
public RcsFeature() {
super();
}
/**{@inheritDoc}*/ /**{@inheritDoc}*/
@Override @Override
public void onFeatureRemoved() { public void onFeatureRemoved() {
@@ -218,4 +395,40 @@ public class RcsFeature extends ImsFeature {
public final IImsRcsFeature getBinder() { public final IImsRcsFeature getBinder() {
return mImsRcsBinder; return mImsRcsBinder;
} }
/**@hide*/
public IRcsFeatureListener getListener() {
synchronized (mLock) {
return mListenerBinder;
}
}
private void setListener(IRcsFeatureListener listener) {
synchronized (mLock) {
mListenerBinder = listener;
if (mListenerBinder != null) {
onFeatureReady();
}
}
}
private RcsPresenceExchangeImplBase getPresenceExchangeInternal() {
synchronized (mLock) {
if (mPresExchange == null) {
mPresExchange = getPresenceExchangeImpl();
mPresExchange.initialize(this);
}
return mPresExchange;
}
}
private RcsSipOptionsImplBase getOptionsExchangeInternal() {
synchronized (mLock) {
if (mSipOptions == null) {
mSipOptions = getOptionsExchangeImpl();
mSipOptions.initialize(this);
}
return mSipOptions;
}
}
} }

View File

@@ -17,6 +17,11 @@
package android.telephony.ims.stub; package android.telephony.ims.stub;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.os.RemoteException;
import android.telephony.ims.ImsException;
import android.telephony.ims.aidl.IRcsFeatureListener;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@@ -72,6 +77,24 @@ public class RcsCapabilityExchange {
}) })
public @interface CommandCode {} public @interface CommandCode {}
private RcsFeature mFeature;
/** @hide */
public final void initialize(RcsFeature feature) {
mFeature = feature;
}
/** @hide */
protected final IRcsFeatureListener getListener() throws ImsException {
IRcsFeatureListener listener = mFeature.getListener();
if (listener == null) {
throw new ImsException("Connection to Framework has not been established, wait for "
+ "onFeatureReady().", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
return mFeature.getListener();
}
/** /**
* Provides the framework with an update as to whether or not a command completed successfully * Provides the framework with an update as to whether or not a command completed successfully
* locally. This includes capabilities requests and updates from the network. If it does not * locally. This includes capabilities requests and updates from the network. If it does not
@@ -82,8 +105,18 @@ public class RcsCapabilityExchange {
* @param code The result of the pending command. If {@link #COMMAND_CODE_SUCCESS}, further * @param code The result of the pending command. If {@link #COMMAND_CODE_SUCCESS}, further
* updates will be sent for this command using the associated operationToken. * updates will be sent for this command using the associated operationToken.
* @param operationToken the token associated with the pending command. * @param operationToken the token associated with the pending command.
* @throws ImsException If this {@link RcsCapabilityExchange} instance is not currently
* connected to the framework. This can happen if the {@link RcsFeature} is not
* {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
* {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
* Telephony stack has crashed.
*/ */
public final void onCommandUpdate(@CommandCode int code, int operationToken) { public final void onCommandUpdate(@CommandCode int code, int operationToken)
throw new UnsupportedOperationException(); throws ImsException {
try {
getListener().onCommandUpdate(code, operationToken);
} catch (RemoteException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
} }
} }

View File

@@ -19,7 +19,11 @@ package android.telephony.ims.stub;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.net.Uri; import android.net.Uri;
import android.os.RemoteException;
import android.telephony.ims.ImsException;
import android.telephony.ims.RcsContactUceCapability; import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
import android.util.Log; import android.util.Log;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@@ -113,54 +117,95 @@ public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange {
* Provide the framework with a subsequent network response update to * Provide the framework with a subsequent network response update to
* {@link #updateCapabilities(RcsContactUceCapability, int)} and * {@link #updateCapabilities(RcsContactUceCapability, int)} and
* {@link #requestCapabilities(List, int)} operations. * {@link #requestCapabilities(List, int)} operations.
*
* @param code The SIP response code sent from the network for the operation token specified. * @param code The SIP response code sent from the network for the operation token specified.
* @param reason The optional reason response from the network. If the network provided no * @param reason The optional reason response from the network. If the network provided no
* reason with the code, the string should be empty. * reason with the code, the string should be empty.
* @param operationToken The token associated with the operation this service is providing a * @param operationToken The token associated with the operation this service is providing a
* response for. * response for.
* @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
* connected to the framework. This can happen if the {@link RcsFeature} is not
* {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
* {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
* Telephony stack has crashed.
*/ */
public final void onNetworkResponse(@PresenceResponseCode int code, @NonNull String reason, public final void onNetworkResponse(@PresenceResponseCode int code, @NonNull String reason,
int operationToken) { int operationToken) throws ImsException {
throw new UnsupportedOperationException(); try {
getListener().onNetworkResponse(code, reason, operationToken);
} catch (RemoteException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
} }
/** /**
* Provides the framework with the requested contacts’ capabilities requested by the framework * Provides the framework with the requested contacts’ capabilities requested by the framework
* using {@link #requestCapabilities(List, int)}. * using {@link #requestCapabilities(List, int)}.
*
* @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
* connected to the framework. This can happen if the {@link RcsFeature} is not
* {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
* {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
* Telephony stack has crashed.
*/ */
public final void onCapabilityRequestResponse(@NonNull List<RcsContactUceCapability> infos, public final void onCapabilityRequestResponse(@NonNull List<RcsContactUceCapability> infos,
int operationToken) { int operationToken) throws ImsException {
throw new UnsupportedOperationException(); try {
getListener().onCapabilityRequestResponsePresence(infos, operationToken);
} catch (RemoteException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
} }
/** /**
* Trigger the framework to provide a capability update using * Trigger the framework to provide a capability update using
* {@link #updateCapabilities(RcsContactUceCapability, int)}. This is typically used when trying * {@link #updateCapabilities(RcsContactUceCapability, int)}.
* to generate an initial PUBLISH for a new subscription to the network.
* <p> * <p>
* The device will cache all presence publications after boot until this method is called once. * This is typically used when trying to generate an initial PUBLISH for a new subscription to
* the network. The device will cache all presence publications after boot until this method is
* called once.
* @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
* connected to the framework. This can happen if the {@link RcsFeature} is not
* {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
* {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
* Telephony stack has crashed.
*/ */
public final void onNotifyUpdateCapabilites() { public final void onNotifyUpdateCapabilites() throws ImsException {
throw new UnsupportedOperationException(); try {
getListener().onNotifyUpdateCapabilities();
} catch (RemoteException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
} }
/** /**
* Notify the framework that the device’s capabilities have been unpublished from the network. * Notify the framework that the device’s capabilities have been unpublished from the network.
*
* @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
* connected to the framework. This can happen if the {@link RcsFeature} is not
* {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
* {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
* Telephony stack has crashed.
*/ */
public final void onUnpublish() { public final void onUnpublish() throws ImsException {
throw new UnsupportedOperationException(); try {
getListener().onUnpublish();
} catch (RemoteException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
} }
/** /**
* The user capabilities of one or multiple contacts have been requested. * The user capabilities of one or multiple contacts have been requested by the framework.
* <p> * <p>
* This must be followed up with one call to {@link #onCommandUpdate(int, int)} with an update * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to
* as to whether or not the command completed as well as subsequent network * indicate whether or not this operation succeeded. If this operation succeeds, network
* updates using {@link #onNetworkResponse(int, String, int)}. When the operation is completed, * response updates should be sent to the framework using
* {@link #onCapabilityRequestResponse(List, int)} should be called with * {@link #onNetworkResponse(int, String, int)}. When the operation is completed,
* the presence information for the contacts specified. * {@link #onCapabilityRequestResponse(List, int)} should be called with the presence
* @param uris A {@link List} of the URIs that the framework is requesting the UCE capabilities * information for the contacts specified.
* for. * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
* capabilities for.
* @param operationToken The token associated with this operation. Updates to this request using * @param operationToken The token associated with this operation. Updates to this request using
* {@link #onCommandUpdate(int, int)}, {@link #onNetworkResponse(int, String, int)}, and * {@link #onCommandUpdate(int, int)}, {@link #onNetworkResponse(int, String, int)}, and
* {@link #onCapabilityRequestResponse(List, int)} must use the same operation token * {@link #onCapabilityRequestResponse(List, int)} must use the same operation token
@@ -169,14 +214,20 @@ public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange {
public void requestCapabilities(@NonNull List<Uri> uris, int operationToken) { public void requestCapabilities(@NonNull List<Uri> uris, int operationToken) {
// Stub - to be implemented by service // Stub - to be implemented by service
Log.w(LOG_TAG, "requestCapabilities called with no implementation."); Log.w(LOG_TAG, "requestCapabilities called with no implementation.");
onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); try {
getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
} catch (RemoteException | ImsException e) {
// Do not do anything, this is a stub implementation.
}
} }
/** /**
* The capabilities of this device have been updated and should be published * The capabilities of this device have been updated and should be published to the network.
* to the network. The framework will expect one {@link #onCommandUpdate(int, int)} call to * <p>
* indicate whether or not this operation failed first as well as network response * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to
* updates to this update using {@link #onNetworkResponse(int, String, int)}. * indicate whether or not this operation succeeded. If this operation succeeds, network
* response updates should be sent to the framework using
* {@link #onNetworkResponse(int, String, int)}.
* @param capabilities The capabilities for this device. * @param capabilities The capabilities for this device.
* @param operationToken The token associated with this operation. Any subsequent * @param operationToken The token associated with this operation. Any subsequent
* {@link #onCommandUpdate(int, int)} or {@link #onNetworkResponse(int, String, int)} * {@link #onCommandUpdate(int, int)} or {@link #onNetworkResponse(int, String, int)}
@@ -186,6 +237,10 @@ public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange {
int operationToken) { int operationToken) {
// Stub - to be implemented by service // Stub - to be implemented by service
Log.w(LOG_TAG, "updateCapabilities called with no implementation."); Log.w(LOG_TAG, "updateCapabilities called with no implementation.");
onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); try {
getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
} catch (RemoteException | ImsException e) {
// Do not do anything, this is a stub implementation.
}
} }
} }

View File

@@ -20,7 +20,11 @@ import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.net.Uri; import android.net.Uri;
import android.os.RemoteException;
import android.telephony.ims.ImsException;
import android.telephony.ims.RcsContactUceCapability; import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
import android.util.Log; import android.util.Log;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@@ -87,10 +91,19 @@ public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
* @param info the contact's UCE capabilities associated with the capability request. * @param info the contact's UCE capabilities associated with the capability request.
* @param operationToken The token associated with the original capability request, set by * @param operationToken The token associated with the original capability request, set by
* {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}. * {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}.
* @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently
* connected to the framework. This can happen if the {@link RcsFeature} is not
* {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
* {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
* Telephony stack has crashed.
*/ */
public final void onCapabilityRequestResponse(@SipResponseCode int code, @NonNull String reason, public final void onCapabilityRequestResponse(@SipResponseCode int code, @NonNull String reason,
@Nullable RcsContactUceCapability info, int operationToken) { @Nullable RcsContactUceCapability info, int operationToken) throws ImsException {
throw new UnsupportedOperationException(); try {
getListener().onCapabilityRequestResponseOptions(code, reason, info, operationToken);
} catch (RemoteException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
} }
/** /**
@@ -104,10 +117,19 @@ public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
* @param operationToken An unique operation token that you have generated that will be returned * @param operationToken An unique operation token that you have generated that will be returned
* by the framework in * by the framework in
* {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)}. * {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)}.
* @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently
* connected to the framework. This can happen if the {@link RcsFeature} is not
* {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
* {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
* Telephony stack has crashed.
*/ */
public final void onRemoteCapabilityRequest(@NonNull Uri contactUri, public final void onRemoteCapabilityRequest(@NonNull Uri contactUri,
@NonNull RcsContactUceCapability remoteInfo, int operationToken) { @NonNull RcsContactUceCapability remoteInfo, int operationToken) throws ImsException {
throw new UnsupportedOperationException(); try {
getListener().onRemoteCapabilityRequest(contactUri, remoteInfo, operationToken);
} catch (RemoteException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
} }
/** /**
@@ -127,7 +149,11 @@ public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
@NonNull RcsContactUceCapability capabilities, int operationToken) { @NonNull RcsContactUceCapability capabilities, int operationToken) {
// Stub - to be implemented by service // Stub - to be implemented by service
Log.w(LOG_TAG, "sendCapabilityRequest called with no implementation."); Log.w(LOG_TAG, "sendCapabilityRequest called with no implementation.");
onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); try {
getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
} catch (RemoteException | ImsException e) {
// Do not do anything, this is a stub implementation.
}
} }
/** /**
@@ -145,7 +171,11 @@ public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
@NonNull RcsContactUceCapability ownCapabilities, int operationToken) { @NonNull RcsContactUceCapability ownCapabilities, int operationToken) {
// Stub - to be implemented by service // Stub - to be implemented by service
Log.w(LOG_TAG, "respondToCapabilityRequest called with no implementation."); Log.w(LOG_TAG, "respondToCapabilityRequest called with no implementation.");
onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); try {
getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
} catch (RemoteException | ImsException e) {
// Do not do anything, this is a stub implementation.
}
} }
/** /**
@@ -164,6 +194,10 @@ public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
@SipResponseCode int code, @NonNull String reason, int operationToken) { @SipResponseCode int code, @NonNull String reason, int operationToken) {
// Stub - to be implemented by service // Stub - to be implemented by service
Log.w(LOG_TAG, "respondToCapabiltyRequestWithError called with no implementation."); Log.w(LOG_TAG, "respondToCapabiltyRequestWithError called with no implementation.");
onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); try {
getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
} catch (RemoteException | ImsException e) {
// Do not do anything, this is a stub implementation.
}
} }
} }