Merge "Modify RCS UCE APIs to use AIDL Interfaces."
diff --git a/Android.bp b/Android.bp
index c194929..0e2287d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -508,6 +508,7 @@
"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 8e1324b..6187e67 100644
--- a/telephony/java/android/telephony/ims/ImsException.java
+++ b/telephony/java/android/telephony/ims/ImsException.java
@@ -39,11 +39,11 @@
*/
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 691cfba..4b98b79 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 0000000..881b477
--- /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 8f89899..3a9979d 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -33,12 +33,8 @@
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 @@
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 @@
/** @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 @@
// 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 @@
*/
@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 @@
/**
* @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 @@
/**
* 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 898ca48..056a0ab 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.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 @@
@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 @@
}
}
- 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 @@
* @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 5fae3ee..f69b434 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 @@
@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 @@
/**@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 @@
*/
@Override
public final RcsImsCapabilities queryCapabilityStatus() {
- throw new UnsupportedOperationException();
+ return new RcsImsCapabilities(super.queryCapabilityStatus());
}
/**
@@ -120,8 +299,11 @@
* 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 @@
* @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 @@
@Override
public void changeEnabledCapabilities(CapabilityChangeRequest request,
CapabilityCallbackProxy c) {
- throw new UnsupportedOperationException();
+ // Base Implementation - Override to provide functionality
}
/**
@@ -192,13 +376,6 @@
return new RcsPresenceExchangeImplBase();
}
- /**
- * Construct a new {@link RcsFeature} instance.
- */
- public RcsFeature() {
- super();
- }
-
/**{@inheritDoc}*/
@Override
public void onFeatureRemoved() {
@@ -218,4 +395,40 @@
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 289fd4c..fda295a 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 @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 @@
* @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 4402470..055fca5 100644
--- a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
@@ -19,7 +19,11 @@
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 @@
* 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.
+ * 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() {
- throw new UnsupportedOperationException();
+ 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.
+ *
+ * @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() {
- throw new UnsupportedOperationException();
+ 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 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 @@
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 3343074..1c68fc0 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.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 @@
* @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 @@
* @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 @@
@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 @@
@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 @@
@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.
+ }
}
}