diff options
10 files changed, 516 insertions, 151 deletions
diff --git a/Android.bp b/Android.bp index c1949295a0d8..0e2287dd27e7 100644 --- a/Android.bp +++ b/Android.bp @@ -508,6 +508,7 @@ java_defaults { "telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl", "telephony/java/android/telephony/ims/aidl/IImsSmsListener.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/IMbmsStreamingSessionCallback.aidl", "telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl", diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java index 8e1324b3be0b..6187e67a6acb 100644 --- a/telephony/java/android/telephony/ims/ImsException.java +++ b/telephony/java/android/telephony/ims/ImsException.java @@ -39,11 +39,11 @@ public final class ImsException extends Exception { */ public static final int CODE_ERROR_UNSPECIFIED = 0; /** - * The operation has failed because there is no {@link ImsService} available to service it. This - * may be due to an {@link ImsService} crash or other illegal state. + * The operation has failed because there is no remote process available to service it. This + * may be due to a process crash or other illegal state. * <p> * 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; diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl index 691cfba9a28c..4b98b79f1095 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl @@ -16,10 +16,38 @@ 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. * {@hide} */ 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); }
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl b/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl new file mode 100644 index 000000000000..881b4776b25b --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl @@ -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); +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index 8f8989909f9f..3a9979d78a55 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -33,12 +33,8 @@ import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; 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 @@ -52,35 +48,6 @@ public abstract class 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 * @hide */ @@ -335,8 +302,8 @@ public abstract class ImsFeature { /** @hide */ protected final Object mLock = new Object(); - private final Set<IImsFeatureStatusCallback> mStatusCallbacks = - Collections.newSetFromMap(new WeakHashMap<>()); + private final RemoteCallbackList<IImsFeatureStatusCallback> mStatusCallbacks = + new RemoteCallbackList<>(); private @ImsState int mState = STATE_UNAVAILABLE; private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks = @@ -397,9 +364,7 @@ public abstract class ImsFeature { // If we have just connected, send queued status. c.notifyImsFeatureStatus(getFeatureState()); // Add the callback if the callback completes successfully without a RemoteException. - synchronized (mLock) { - mStatusCallbacks.add(c); - } + mStatusCallbacks.register(c); } catch (RemoteException e) { Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage()); } @@ -411,29 +376,21 @@ public abstract class ImsFeature { */ @VisibleForTesting public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) { - synchronized (mLock) { - mStatusCallbacks.remove(c); - } + mStatusCallbacks.unregister(c); } /** * Internal method called by ImsFeature when setFeatureState has changed. */ private void notifyFeatureState(@ImsState int state) { - synchronized (mLock) { - for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator(); - iter.hasNext(); ) { - IImsFeatureStatusCallback callback = iter.next(); - try { - Log.i(LOG_TAG, "notifying ImsFeatureState=" + state); - callback.notifyImsFeatureStatus(state); - } catch (RemoteException e) { - // remove if the callback is no longer alive. - iter.remove(); - Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage()); - } + mStatusCallbacks.broadcast((c) -> { + try { + c.notifyImsFeatureStatus(state); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " notifyFeatureState() - Skipping " + + "callback."); } - } + }); } /** @@ -452,10 +409,23 @@ public abstract class ImsFeature { /** * @hide */ - public final void removeCapabilityCallback(IImsCapabilityCallback c) { + final void removeCapabilityCallback(IImsCapabilityCallback 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. * @hide @@ -484,31 +454,36 @@ public abstract class ImsFeature { /** * 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 */ - protected final void notifyCapabilitiesStatusChanged(Capabilities c) { + protected final void notifyCapabilitiesStatusChanged(Capabilities caps) { synchronized (mLock) { - mCapabilityStatus = c.copy(); + mCapabilityStatus = caps.copy(); } - int count = mCapabilityCallbacks.beginBroadcast(); - try { - for (int i = 0; i < count; i++) { - try { - mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged( - c.mCapabilities); - } catch (RemoteException e) { - Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " + - "callback."); - } + mCapabilityCallbacks.broadcast((callback) -> { + try { + callback.onCapabilitiesStatusChanged(caps.mCapabilities); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " notifyCapabilitiesStatusChanged() - Skipping " + + "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 * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities * in the {@link CapabilityChangeRequest} are not able to be completed due to an error, diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index 898ca48cd1df..056a0abe7a29 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -37,7 +37,6 @@ import android.telephony.ims.stub.ImsMultiEndpointImplBase; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.telephony.ims.stub.ImsSmsImplBase; import android.telephony.ims.stub.ImsUtImplBase; -import android.util.Log; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsEcbm; @@ -154,17 +153,13 @@ public class MmTelFeature extends ImsFeature { @Override public void changeCapabilitiesConfiguration(CapabilityChangeRequest request, IImsCapabilityCallback c) { - synchronized (mLock) { - MmTelFeature.this.requestChangeEnabledCapabilities(request, c); - } + MmTelFeature.this.requestChangeEnabledCapabilities(request, c); } @Override public void queryCapabilityConfiguration(int capability, int radioTech, IImsCapabilityCallback c) { - synchronized (mLock) { - queryCapabilityConfigurationInternal(capability, radioTech, c); - } + queryCapabilityConfigurationInternal(capability, radioTech, c); } @Override @@ -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 * 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. * @return true if the capability is enabled, false otherwise. */ + @Override public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { // Base implementation - Override to provide functionality diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index 5fae3eebbfc6..f69b434eb120 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -16,14 +16,32 @@ package android.telephony.ims.feature; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; +import android.annotation.NonNull; 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.IRcsFeatureListener; +import android.telephony.ims.stub.ImsRegistrationImplBase; import android.telephony.ims.stub.RcsPresenceExchangeImplBase; 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.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 @@ -33,10 +51,132 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public class RcsFeature extends ImsFeature { - /**{@inheritDoc}*/ - private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() { - // Empty Default Implementation. - }; + private static final String LOG_TAG = "RcsFeature"; + + 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 @@ -81,27 +221,66 @@ public class RcsFeature extends ImsFeature { /**@hide*/ public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) { + super(capabilities); + } + /**@hide*/ + private RcsImsCapabilities(Capabilities c) { + super(c.getMask()); } /**@hide*/ @Override public void addCapabilities(@RcsImsCapabilityFlag int capabilities) { - + super.addCapabilities(capabilities); } /**@hide*/ @Override public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) { - + super.removeCapabilities(capabilities); } /**@hide*/ @Override 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 * set, the {@link RcsFeature} has brought up the capability and is ready for framework @@ -111,7 +290,7 @@ public class RcsFeature extends ImsFeature { */ @Override 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. * @hide */ - public final void notifyCapabilitiesStatusChanged(RcsImsCapabilities c) { - throw new UnsupportedOperationException(); + public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) { + if (c == null) { + throw new IllegalArgumentException("RcsImsCapabilities must be non-null!"); + } + super.notifyCapabilitiesStatusChanged(c); } /** @@ -133,8 +315,10 @@ public class RcsFeature extends ImsFeature { * @hide */ public boolean queryCapabilityConfiguration( - @RcsImsCapabilities.RcsImsCapabilityFlag int capability) { - throw new UnsupportedOperationException(); + @RcsImsCapabilities.RcsImsCapabilityFlag int capability, + @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 @@ -155,7 +339,7 @@ public class RcsFeature extends ImsFeature { @Override public void changeEnabledCapabilities(CapabilityChangeRequest request, CapabilityCallbackProxy c) { - throw new UnsupportedOperationException(); + // Base Implementation - Override to provide functionality } /** @@ -192,13 +376,6 @@ public class RcsFeature extends ImsFeature { return new RcsPresenceExchangeImplBase(); } - /** - * Construct a new {@link RcsFeature} instance. - */ - public RcsFeature() { - super(); - } - /**{@inheritDoc}*/ @Override public void onFeatureRemoved() { @@ -218,4 +395,40 @@ public class RcsFeature extends ImsFeature { public final IImsRcsFeature getBinder() { 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; + } + } } diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java index 289fd4c8a134..fda295a27111 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java @@ -17,6 +17,11 @@ package android.telephony.ims.stub; 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.RetentionPolicy; @@ -72,6 +77,24 @@ public class RcsCapabilityExchange { }) 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 * 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 * updates will be sent for this command using the associated operationToken. * @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) { - throw new UnsupportedOperationException(); + public final void onCommandUpdate(@CommandCode int code, int operationToken) + throws ImsException { + try { + getListener().onCommandUpdate(code, operationToken); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } } } diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java index 44024703042d..055fca57a628 100644 --- a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java @@ -19,7 +19,11 @@ package android.telephony.ims.stub; import android.annotation.IntDef; import android.annotation.NonNull; import android.net.Uri; +import android.os.RemoteException; +import android.telephony.ims.ImsException; import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.feature.RcsFeature; import android.util.Log; import java.lang.annotation.Retention; @@ -113,54 +117,95 @@ public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange { * Provide the framework with a subsequent network response update to * {@link #updateCapabilities(RcsContactUceCapability, int)} and * {@link #requestCapabilities(List, int)} operations. + * * @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 * reason with the code, the string should be empty. * @param operationToken The token associated with the operation this service is providing a * 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, - int operationToken) { - throw new UnsupportedOperationException(); + int operationToken) throws ImsException { + 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 - * 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, - int operationToken) { - throw new UnsupportedOperationException(); + int operationToken) throws ImsException { + 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 - * {@link #updateCapabilities(RcsContactUceCapability, int)}. This is typically used when trying - * to generate an initial PUBLISH for a new subscription to the network. + * {@link #updateCapabilities(RcsContactUceCapability, int)}. * <p> - * The device will cache all presence publications after boot until this method is called once. - */ - public final void onNotifyUpdateCapabilites() { - throw new UnsupportedOperationException(); + * 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() throws ImsException { + 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. - */ - public final void onUnpublish() { - throw new UnsupportedOperationException(); + * + * @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() throws ImsException { + 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> - * This must be followed up with one call to {@link #onCommandUpdate(int, int)} with an update - * as to whether or not the command completed as well as subsequent network - * updates using {@link #onNetworkResponse(int, String, int)}. When the operation is completed, - * {@link #onCapabilityRequestResponse(List, int)} should be called with - * the presence information for the contacts specified. - * @param uris A {@link List} of the URIs that the framework is requesting the UCE capabilities - * for. + * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to + * 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)}. When the operation is completed, + * {@link #onCapabilityRequestResponse(List, int)} should be called with the presence + * information for the contacts specified. + * @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 * {@link #onCommandUpdate(int, int)}, {@link #onNetworkResponse(int, String, int)}, and * {@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) { // Stub - to be implemented by service 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 - * to the network. The framework will expect one {@link #onCommandUpdate(int, int)} call to - * indicate whether or not this operation failed first as well as network response - * updates to this update using {@link #onNetworkResponse(int, String, int)}. + * The capabilities of this device have been updated and should be published to the network. + * <p> + * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to + * 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 operationToken The token associated with this operation. Any subsequent * {@link #onCommandUpdate(int, int)} or {@link #onNetworkResponse(int, String, int)} @@ -186,6 +237,10 @@ public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange { int operationToken) { // Stub - to be implemented by service 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. + } } } diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java index 3343074ea52c..1c68fc08529e 100644 --- a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java @@ -20,7 +20,11 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.Uri; +import android.os.RemoteException; +import android.telephony.ims.ImsException; import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.feature.RcsFeature; import android.util.Log; 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 operationToken The token associated with the original capability request, set by * {@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, - @Nullable RcsContactUceCapability info, int operationToken) { - throw new UnsupportedOperationException(); + @Nullable RcsContactUceCapability info, int operationToken) throws ImsException { + 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 * by the framework in * {@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, - @NonNull RcsContactUceCapability remoteInfo, int operationToken) { - throw new UnsupportedOperationException(); + @NonNull RcsContactUceCapability remoteInfo, int operationToken) throws ImsException { + 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) { // Stub - to be implemented by service 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) { // Stub - to be implemented by service 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) { // Stub - to be implemented by service 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. + } } } |