RCS Provisioning APIs for Single Registration

Bug: 154864150
Test: atest FrameworksTelephonyTests:com.telephony.ims.RcsConfigTest
Test: atest TeleServiceTests:com.android.phone.RcsProvisioningMonitorTest
Test: atest CtsTelephonyTestCases:android.telephony.ims.cts.ImsServiceTest
Merged-In: Ie9445dd450d175e0dc94b63b487dda5cde729123
Change-Id: Ie9445dd450d175e0dc94b63b487dda5cde729123
This commit is contained in:
Hui Wang
2020-10-31 05:09:59 +00:00
parent 777f0ea905
commit e92ddfef4f
13 changed files with 1044 additions and 2 deletions

View File

@@ -40253,6 +40253,7 @@ package android.telephony {
field public static final String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool";
field public static final String KEY_TTY_SUPPORTED_BOOL = "tty_supported_bool";
field public static final String KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY = "unloggable_numbers_string_array";
field public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
field public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
field public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
field public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";

View File

@@ -11441,18 +11441,29 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isRcsVolteSingleRegistrationCapable() throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerRcsProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRcsClientConfiguration(@NonNull android.telephony.ims.RcsClientConfiguration) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void triggerRcsReconfiguration();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterRcsProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback);
field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE = "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE";
field public static final String EXTRA_STATUS = "android.telephony.ims.extra.STATUS";
field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.ims.extra.SUBSCRIPTION_ID";
field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43
field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
field public static final int STATUS_CAPABLE = 0; // 0x0
field public static final int STATUS_CARRIER_NOT_CAPABLE = 2; // 0x2
field public static final int STATUS_DEVICE_NOT_CAPABLE = 1; // 0x1
field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
}
@@ -11463,6 +11474,27 @@ package android.telephony.ims {
method public void onProvisioningStringChanged(int, @NonNull String);
}
public static class ProvisioningManager.RcsProvisioningCallback {
ctor public ProvisioningManager.RcsProvisioningCallback();
method public void onAutoConfigurationErrorReceived(int, @NonNull String);
method public void onConfigurationChanged(@NonNull byte[]);
method public void onConfigurationReset();
method public void onRemoved();
}
public final class RcsClientConfiguration implements android.os.Parcelable {
ctor public RcsClientConfiguration(@NonNull String, @NonNull String, @NonNull String, @NonNull String);
method public int describeContents();
method @NonNull public String getClientVendor();
method @NonNull public String getClientVersion();
method @NonNull public String getRcsProfile();
method @NonNull public String getRcsVersion();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsClientConfiguration> CREATOR;
field public static final String RCS_PROFILE_1_0 = "UP_1.0";
field public static final String RCS_PROFILE_2_3 = "UP_2.3";
}
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
}
@@ -11750,11 +11782,15 @@ package android.telephony.ims.stub {
ctor public ImsConfigImplBase();
method public int getConfigInt(int);
method public String getConfigString(int);
method public final void notifyAutoConfigurationErrorReceived(int, @NonNull String);
method public final void notifyProvisionedValueChanged(int, int);
method public final void notifyProvisionedValueChanged(int, String);
method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
method public void notifyRcsAutoConfigurationRemoved();
method public int setConfig(int, int);
method public int setConfig(int, String);
method public void setRcsClientConfiguration(@NonNull android.telephony.ims.RcsClientConfiguration);
method public void triggerAutoConfiguration();
field public static final int CONFIG_RESULT_FAILED = 1; // 0x1
field public static final int CONFIG_RESULT_SUCCESS = 0; // 0x0
field public static final int CONFIG_RESULT_UNKNOWN = -1; // 0xffffffff

View File

@@ -5270,5 +5270,13 @@ public final class Telephony {
* @hide
*/
public static final String COLUMN_ALLOWED_NETWORK_TYPES = "allowed_network_types";
/**
* TelephonyProvider column name for RCS configuration.
* <p>TYPE: BLOB
*
* @hide
*/
public static final String COLUMN_RCS_CONFIG = "rcs_config";
}
}

View File

@@ -4018,6 +4018,12 @@ public class CarrierConfigManager {
public static final String KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED =
"use_lower_mtu_value_if_both_received";
/**
* Indicates if auto-configuration server is used for the RCS config
* Reference: GSMA RCC.14
*/
public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -4561,6 +4567,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false);
}
/**

View File

@@ -21,6 +21,7 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.WorkerThread;
@@ -31,6 +32,7 @@ import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.aidl.IRcsConfigCallback;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature;
import android.telephony.ims.stub.ImsConfigImplBase;
@@ -935,6 +937,115 @@ public class ProvisioningManager {
private int mSubId;
/**
* The callback for RCS provisioning changes.
*/
public static class RcsProvisioningCallback {
private static class CallbackBinder extends IRcsConfigCallback.Stub {
private final RcsProvisioningCallback mLocalCallback;
private Executor mExecutor;
private CallbackBinder(RcsProvisioningCallback localCallback) {
mLocalCallback = localCallback;
}
@Override
public void onConfigurationChanged(byte[] configXml) {
final long identity = Binder.clearCallingIdentity();
try {
mExecutor.execute(() -> mLocalCallback.onConfigurationChanged(configXml));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void onAutoConfigurationErrorReceived(int errorCode, String errorString) {
final long identity = Binder.clearCallingIdentity();
try {
mExecutor.execute(() -> mLocalCallback.onAutoConfigurationErrorReceived(
errorCode, errorString));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void onConfigurationReset() {
final long identity = Binder.clearCallingIdentity();
try {
mExecutor.execute(() -> mLocalCallback.onConfigurationReset());
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void onRemoved() {
final long identity = Binder.clearCallingIdentity();
try {
mExecutor.execute(() -> mLocalCallback.onRemoved());
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private void setExecutor(Executor executor) {
mExecutor = executor;
}
}
private final CallbackBinder mBinder = new CallbackBinder(this);
/**
* RCS configuration received via OTA provisioning. Configuration may change
* due to various triggers defined in GSMA RCC.14 for ACS(auto configuration
* server) or other operator defined triggers. If RCS provisioning is already
* completed at the time of callback registration, then this method shall be
* invoked with the current configuration
* @param configXml The RCS configurationXML received OTA.
*/
public void onConfigurationChanged(@NonNull byte[] configXml) {}
/**
* Errors during autoconfiguration connection setup are notified by the
* ACS(auto configuration server) client using this interface.
* @param errorCode HTTP error received during connection setup defined in
* GSMA RCC.14 2.4.3, like {@link java.net.HttpURLConnection#HTTP_UNAUTHORIZED},
* {@link java.net.HttpURLConnection#HTTP_FORBIDDEN}, etc.
* @param errorString reason phrase received with the error
*/
public void onAutoConfigurationErrorReceived(int errorCode,
@NonNull String errorString) {}
/**
* When the previously valid RCS configuration is cleaned up by telephony for
* any case like SIM removed, default messaging application changed, etc.,
* this method will be invoked to notify the application regarding this change.
*/
public void onConfigurationReset() {}
/**
* When the RCS application is no longer the Default messaging application,
* or when the subscription associated with this callback is removed (SIM
* removed, ESIM swap,etc...), callback will automatically be removed and
* the below method is invoked. There is a possibility that the method is
* invoked after the subscription has become inactive
*/
public void onRemoved() {}
/**@hide*/
public final IRcsConfigCallback getBinder() {
return mBinder;
}
/**@hide*/
public void setExecutor(Executor executor) {
mBinder.setExecutor(executor);
}
}
/**
* Create a new {@link ProvisioningManager} for the subscription specified.
*
@@ -1207,6 +1318,174 @@ public class ProvisioningManager {
}
/**
* Provides the single registration capability of the device and the carrier.
*
* <p>This intent only provides the capability and not the current provisioning status of
* the RCS VoLTE single registration feature. Only default messaging application may receive
* the intent.
*
* <p>Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to specify the subscription index for which
* the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration
* status.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE =
"android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE";
/**
* Integer extra to specify subscription index.
*/
public static final String EXTRA_SUBSCRIPTION_ID =
"android.telephony.ims.extra.SUBSCRIPTION_ID";
/**
* Integer extra to specify RCS single registration status
*
* <p>The value can be {@link #STATUS_CAPABLE}, {@link #STATUS_DEVICE_NOT_CAPABLE},
* {@link #STATUS_CARRIER_NOT_CAPABLE}, or bitwise OR of
* {@link #STATUS_DEVICE_NOT_CAPABLE} and {@link #STATUS_CARRIER_NOT_CAPABLE}.
*/
public static final String EXTRA_STATUS = "android.telephony.ims.extra.STATUS";
/**
* RCS VoLTE single registration is supported by the device and carrier.
*/
public static final int STATUS_CAPABLE = 0;
/**
* RCS VoLTE single registration is not supported by the device.
*/
public static final int STATUS_DEVICE_NOT_CAPABLE = 0x01;
/**
* RCS VoLTE single registration is not supported by the carrier
*/
public static final int STATUS_CARRIER_NOT_CAPABLE = 0x01 << 1;
/**
* Provide the client configuration parameters of the RCS application.
*
* <p>When this application is also the default messaging application, and RCS
* provisioning is done using autoconfiguration, then these parameters shall be
* sent in the HTTP get request to fetch the RCS provisioning. RCS client
* configuration must be provided by the application before registering for the
* provisioning status events {@link #registerRcsProvisioningChangedCallback()}
* @param rcc RCS client configuration {@link RcsClientConfiguration}
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setRcsClientConfiguration(
@NonNull RcsClientConfiguration rcc) throws ImsException {
try {
getITelephony().setRcsClientConfiguration(mSubId, rcc);
} catch (ServiceSpecificException e) {
throw new ImsException(e.getMessage(), e.errorCode);
} catch (RemoteException | IllegalStateException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
/**
* Returns a flag to indicate if the device software and the carrier
* have the capability to support RCS Volte single IMS registration.
* @return true if this single registration is capable, false otherwise
* @throws ImsException If the remote ImsService is not available for
* any reason or the subscription associated with this instance is no
* longer active. See {@link ImsException#getCode()} for more
* information.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
try {
return getITelephony().isRcsVolteSingleRegistrationCapable(mSubId);
} catch (RemoteException | IllegalStateException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
/**
* Registers a new {@link RcsProvisioningCallback} to listen to changes to
* RCS provisioning xml.
*
* <p>RCS application must be the default messaging application and must
* have already registered its {@link RcsClientConfiguration} by using
* {@link #setRcsClientConfiguration} before it registers the provisioning
* callback. If ProvisioningManager has a valid RCS configuration at the
* time of callback registration and a reconfiguration is not required
* due to RCS client parameters change, then the callback shall be invoked
* immediately with the xml.
* When the subscription associated with this callback is removed (SIM removed,
* ESIM swap,etc...), this callback will automatically be removed.
*
* @param executor The {@link Executor} to call the callback methods on
* @param callback The rcs provisioning callback to be registered.
* @see #unregisterRcsProvisioningChangedCallback(RcsProvisioningCallback)
* @see SubscriptionManager.OnSubscriptionsChangedListener
* @throws IllegalArgumentException if the subscription associated with this
* callback is not active (SIM is not inserted, ESIM inactive) or the
* subscription is invalid.
* @throws ImsException if the subscription associated with this callback is
* valid, but the {@link ImsService} associated with the subscription is not
* available. This can happen if the service crashed, for example.
* It shall also throw this exception when the RCS client parameters for the
* application are not valid. In that case application must set the client
* params (See {@link #setRcsClientConfiguration()}) and re register the
* callback.
* See {@link ImsException#getCode()} for a more detailed reason.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void registerRcsProvisioningChangedCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull RcsProvisioningCallback callback) throws ImsException {
callback.setExecutor(executor);
try {
getITelephony().registerRcsProvisioningChangedCallback(mSubId, callback.getBinder());
} catch (ServiceSpecificException e) {
throw new ImsException(e.getMessage(), e.errorCode);
} catch (RemoteException | IllegalStateException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
/**
* Unregister an existing {@link RcsProvisioningCallback}. Application can
* unregister when its no longer interested in the provisioning updates
* like when a user disables RCS from the UI/settings.
* When the subscription associated with this callback is removed (SIM
* removed, ESIM swap, etc...), this callback will automatically be
* removed. If this method is called for an inactive subscription, it
* will result in a no-op.
* @param callback The existing {@link RcsProvisioningCallback} to be
* removed.
* @see #registerRcsProvisioningChangedCallback(RcsClientConfiguration,
* Executor, RcsProvisioningCallback) @throws IllegalArgumentException
* if the subscription associated with this callback is invalid.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterRcsProvisioningChangedCallback(
@NonNull RcsProvisioningCallback callback) {
try {
getITelephony().unregisterRcsProvisioningChangedCallback(
mSubId, callback.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/**
* Reconfiguration triggered by the RCS application. Most likely cause
* is the 403 forbidden to a HTTP request.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void triggerRcsReconfiguration() {
try {
getITelephony().triggerRcsReconfiguration(mSubId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(
TelephonyFrameworkInitializer

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2020 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;
parcelable RcsClientConfiguration;

View File

@@ -0,0 +1,162 @@
/*
* 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;
import android.annotation.NonNull;
import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* The container of RCS application related configs.
*
* @hide
*/
@SystemApi
public final class RcsClientConfiguration implements Parcelable {
/**@hide*/
@StringDef(prefix = "RCS_PROFILE_",
value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3})
public @interface StringRcsProfile {}
/**
* RCS profile UP 1.0
*/
public static final String RCS_PROFILE_1_0 = "UP_1.0";
/**
* RCS profile UP 2.3
*/
public static final String RCS_PROFILE_2_3 = "UP_2.3";
private String mRcsVersion;
private String mRcsProfile;
private String mClientVendor;
private String mClientVersion;
/**
* Create a RcsClientConfiguration object.
* Default messaging application must pass a valid configuration object
* @param rcsVersion The parameter identifies the RCS version supported
* by the client. Refer to GSMA RCC.07 "rcs_version" parameter.
* @param rcsProfile Identifies a fixed set of RCS services that are
* supported by the client. See {@link #RCS_PROFILE_1_0 } or
* {@link #RCS_PROFILE_2_3 }
* @param clientVendor Identifies the vendor providing the RCS client.
* @param clientVersion Identifies the RCS client version. Refer to GSMA
* RCC.07 "client_version" parameter.
* Example:client_version=RCSAndrd-1.0
*/
public RcsClientConfiguration(@NonNull String rcsVersion,
@NonNull @StringRcsProfile String rcsProfile,
@NonNull String clientVendor, @NonNull String clientVersion) {
mRcsVersion = rcsVersion;
mRcsProfile = rcsProfile;
mClientVendor = clientVendor;
mClientVersion = clientVersion;
}
/**
* Returns RCS version supported.
*/
public @NonNull String getRcsVersion() {
return mRcsVersion;
}
/**
* Returns RCS profile supported.
*/
public @NonNull @StringRcsProfile String getRcsProfile() {
return mRcsProfile;
}
/**
* Returns the name of the vendor providing the RCS client.
*/
public @NonNull String getClientVendor() {
return mClientVendor;
}
/**
* Returns the RCS client version.
*/
public @NonNull String getClientVersion() {
return mClientVersion;
}
/**
* {@link Parcelable#writeToParcel}
*/
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeString(mRcsVersion);
out.writeString(mRcsProfile);
out.writeString(mClientVendor);
out.writeString(mClientVersion);
}
/**
* {@link Parcelable.Creator}
*
*/
public static final @android.annotation.NonNull Parcelable.Creator<
RcsClientConfiguration> CREATOR = new Creator<RcsClientConfiguration>() {
@Override
public RcsClientConfiguration createFromParcel(Parcel in) {
String rcsVersion = in.readString();
String rcsProfile = in.readString();
String clientVendor = in.readString();
String clientVersion = in.readString();
return new RcsClientConfiguration(rcsVersion, rcsProfile,
clientVendor, clientVersion);
}
@Override
public RcsClientConfiguration[] newArray(int size) {
return new RcsClientConfiguration[size];
}
};
/**
* {@link Parcelable#describeContents}
*/
@Override
public int describeContents() {
return 0;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof RcsClientConfiguration)) {
return false;
}
RcsClientConfiguration other = (RcsClientConfiguration) obj;
return mRcsVersion.equals(other.mRcsVersion) && mRcsProfile.equals(other.mRcsProfile)
&& mClientVendor.equals(other.mClientVendor)
&& mClientVersion.equals(other.mClientVersion);
}
@Override
public int hashCode() {
return Objects.hash(mRcsVersion, mRcsProfile, mClientVendor, mClientVersion);
}
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2020 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;
parcelable RcsConfig;

View File

@@ -0,0 +1,306 @@
/*
* Copyright (C) 2020 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;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Telephony.SimInfo;
import android.text.TextUtils;
import com.android.telephony.Rlog;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/**
* RCS config data and methods to process the config
* @hide
*/
public final class RcsConfig implements Parcelable {
private static final String LOG_TAG = "RcsConfig";
private static final boolean DBG = Build.IS_ENG;
private final HashMap<String, String> mValues = new HashMap<>();
private RcsConfig(HashMap<String, String> values) {
mValues.putAll(values);
}
public RcsConfig(byte[] data) throws IllegalArgumentException {
if (data == null || data.length == 0) {
throw new IllegalArgumentException("Empty data");
}
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput(inputStream, null);
int eventType = xpp.getEventType();
String tag = null;
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
tag = xpp.getName().trim();
} else if (eventType == XmlPullParser.END_TAG) {
tag = null;
} else if (eventType == XmlPullParser.TEXT) {
String value = xpp.getText().trim();
if (!TextUtils.isEmpty(tag) && !TextUtils.isEmpty(value)) {
mValues.put(tag, value);
}
}
eventType = xpp.next();
}
} catch (IOException | XmlPullParserException e) {
throw new IllegalArgumentException(e);
} finally {
try {
inputStream.close();
} catch (IOException e) {
loge("error to close input stream, skip.");
}
}
}
/**
* Retrieve a String value of the config item with the tag
*
* @param tag The name of the config to retrieve.
* @param defaultVal Value to return if the config does not exist.
*
* @return Returns the config value if it exists, or defaultVal.
*/
public @Nullable String getString(@NonNull String tag, @Nullable String defaultVal) {
return mValues.containsKey(tag) ? mValues.get(tag) : defaultVal;
}
/**
* Retrieve a int value of the config item with the tag
*
* @param tag The name of the config to retrieve.
* @param defaultVal Value to return if the config does not exist or not valid.
*
* @return Returns the config value if it exists and is a valid int, or defaultVal.
*/
public int getInteger(@NonNull String tag, int defaultVal) {
try {
return Integer.parseInt(mValues.get(tag));
} catch (NumberFormatException e) {
logd("error to getInteger for " + tag + " due to " + e);
}
return defaultVal;
}
/**
* Retrieve a boolean value of the config item with the tag
*
* @param tag The name of the config to retrieve.
* @param defaultVal Value to return if the config does not exist.
*
* @return Returns the config value if it exists, or defaultVal.
*/
public boolean getBoolean(@NonNull String tag, boolean defaultVal) {
if (!mValues.containsKey(tag)) {
return defaultVal;
}
return Boolean.parseBoolean(mValues.get(tag));
}
/**
* Check whether the config item exists
*
* @param tag The name of the config to retrieve.
*
* @return Returns true if it exists, or false.
*/
public boolean hasConfig(@NonNull String tag) {
return mValues.containsKey(tag);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("[RCS Config]");
if (DBG) {
mValues.forEach((t, v) -> {
sb.append("\n");
sb.append(t);
sb.append(" : ");
sb.append(v);
});
}
return sb.toString();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof RcsConfig)) {
return false;
}
RcsConfig other = (RcsConfig) obj;
return mValues.equals(other.mValues);
}
@Override
public int hashCode() {
return mValues.hashCode();
}
/**
* compress the gzip format data
*/
public static @Nullable byte[] compressGzip(@NonNull byte[] data) {
if (data == null || data.length == 0) {
return data;
}
byte[] out = null;
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
GZIPOutputStream gzipCompressingStream =
new GZIPOutputStream(outputStream);
gzipCompressingStream.write(data);
gzipCompressingStream.close();
out = outputStream.toByteArray();
outputStream.close();
} catch (IOException e) {
loge("Error to compressGzip due to " + e);
}
return out;
}
/**
* decompress the gzip format data
*/
public static @Nullable byte[] decompressGzip(@NonNull byte[] data) {
if (data == null || data.length == 0) {
return data;
}
byte[] out = null;
try {
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GZIPInputStream gzipDecompressingStream =
new GZIPInputStream(inputStream);
byte[] buf = new byte[1024];
int size = gzipDecompressingStream.read(buf);
while (size >= 0) {
outputStream.write(buf, 0, size);
size = gzipDecompressingStream.read(buf);
}
gzipDecompressingStream.close();
inputStream.close();
out = outputStream.toByteArray();
outputStream.close();
} catch (IOException e) {
loge("Error to decompressGzip due to " + e);
}
return out;
}
/**
* save the config to siminfo db. It is only used internally.
*/
public static void updateConfigForSub(@NonNull Context cxt, int subId,
@NonNull byte[] config, boolean isCompressed) {
//always store gzip compressed data
byte[] data = isCompressed ? config : compressGzip(config);
ContentValues values = new ContentValues();
values.put(SimInfo.COLUMN_RCS_CONFIG, data);
cxt.getContentResolver().update(SimInfo.CONTENT_URI, values,
SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null);
}
/**
* load the config from siminfo db. It is only used internally.
*/
public static @Nullable byte[] loadRcsConfigForSub(@NonNull Context cxt,
int subId, boolean isCompressed) {
byte[] data = null;
Cursor cursor = cxt.getContentResolver().query(SimInfo.CONTENT_URI, null,
SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
data = cursor.getBlob(cursor.getColumnIndexOrThrow(SimInfo.COLUMN_RCS_CONFIG));
}
} catch (Exception e) {
loge("error to load rcs config for sub:" + subId + " due to " + e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return isCompressed ? data : decompressGzip(data);
}
/**
* {@link Parcelable#writeToParcel}
*/
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeMap(mValues);
}
/**
* {@link Parcelable.Creator}
*
*/
public static final @NonNull Parcelable.Creator<RcsConfig>
CREATOR = new Creator<RcsConfig>() {
@Override
public RcsConfig createFromParcel(Parcel in) {
HashMap<String, String> values = in.readHashMap(null);
return values == null ? null : new RcsConfig(values);
}
@Override
public RcsConfig[] newArray(int size) {
return new RcsConfig[size];
}
};
/**
* {@link Parcelable#describeContents}
*/
public int describeContents() {
return 0;
}
private static void logd(String msg) {
Rlog.d(LOG_TAG, msg);
}
private static void loge(String msg) {
Rlog.e(LOG_TAG, msg);
}
}

View File

@@ -18,8 +18,9 @@
package android.telephony.ims.aidl;
import android.os.PersistableBundle;
import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.aidl.IRcsConfigCallback;
import android.telephony.ims.RcsClientConfiguration;
import com.android.ims.ImsConfigListener;
@@ -41,4 +42,9 @@ interface IImsConfig {
int setConfigString(int item, String value);
void updateImsCarrierConfigs(in PersistableBundle bundle);
void notifyRcsAutoConfigurationReceived(in byte[] config, boolean isCompressed);
void notifyRcsAutoConfigurationRemoved();
void addRcsConfigCallback(IRcsConfigCallback c);
void removeRcsConfigCallback(IRcsConfigCallback c);
void triggerRcsReconfiguration();
void setRcsClientConfiguration(in RcsClientConfiguration rcc);
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2020 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;
/**
* The callback for RCS provisioning changes.
* {@hide}
*/
oneway interface IRcsConfigCallback {
void onConfigurationChanged(in byte[] config);
void onAutoConfigurationErrorReceived(int errorCode, String errorString);
void onConfigurationReset();
void onRemoved();
}

View File

@@ -23,8 +23,11 @@ import android.content.Context;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.RcsClientConfiguration;
import android.telephony.ims.RcsConfig;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.aidl.IRcsConfigCallback;
import android.util.Log;
import com.android.ims.ImsConfig;
@@ -202,7 +205,13 @@ public class ImsConfigImplBase {
@Override
public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)
throws RemoteException {
getImsConfigImpl().notifyRcsAutoConfigurationReceived(config, isCompressed);
getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed);
}
@Override
public void notifyRcsAutoConfigurationRemoved()
throws RemoteException {
getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved();
}
private void notifyImsConfigChanged(int item, int value) throws RemoteException {
@@ -228,6 +237,26 @@ public class ImsConfigImplBase {
notifyImsConfigChanged(item, value);
}
}
@Override
public void addRcsConfigCallback(IRcsConfigCallback c) throws RemoteException {
getImsConfigImpl().addRcsConfigCallback(c);
}
@Override
public void removeRcsConfigCallback(IRcsConfigCallback c) throws RemoteException {
getImsConfigImpl().removeRcsConfigCallback(c);
}
@Override
public void triggerRcsReconfiguration() throws RemoteException {
getImsConfigImpl().triggerAutoConfiguration();
}
@Override
public void setRcsClientConfiguration(RcsClientConfiguration rcc) throws RemoteException {
getImsConfigImpl().setRcsClientConfiguration(rcc);
}
}
/**
@@ -257,6 +286,9 @@ public class ImsConfigImplBase {
private final RemoteCallbackListExt<IImsConfigCallback> mCallbacks =
new RemoteCallbackListExt<>();
private final RemoteCallbackListExt<IRcsConfigCallback> mRcsCallbacks =
new RemoteCallbackListExt<>();
private byte[] mRcsConfigData;
ImsConfigStub mImsConfigStub;
/**
@@ -320,6 +352,50 @@ public class ImsConfigImplBase {
});
}
private void addRcsConfigCallback(IRcsConfigCallback c) {
mRcsCallbacks.register(c);
if (mRcsConfigData != null) {
try {
c.onConfigurationChanged(mRcsConfigData);
} catch (RemoteException e) {
Log.w(TAG, "dead binder to call onConfigurationChanged, skipping.");
}
}
}
private void removeRcsConfigCallback(IRcsConfigCallback c) {
mRcsCallbacks.unregister(c);
}
private void onNotifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) {
mRcsConfigData = isCompressed ? RcsConfig.decompressGzip(config) : config;
// can be null in testing
if (mRcsCallbacks != null) {
mRcsCallbacks.broadcastAction(c -> {
try {
c.onConfigurationChanged(mRcsConfigData);
} catch (RemoteException e) {
Log.w(TAG, "dead binder in notifyRcsAutoConfigurationReceived, skipping.");
}
});
}
notifyRcsAutoConfigurationReceived(config, isCompressed);
}
private void onNotifyRcsAutoConfigurationRemoved() {
mRcsConfigData = null;
if (mRcsCallbacks != null) {
mRcsCallbacks.broadcastAction(c -> {
try {
c.onConfigurationReset();
} catch (RemoteException e) {
Log.w(TAG, "dead binder in notifyRcsAutoConfigurationRemoved, skipping.");
}
});
}
notifyRcsAutoConfigurationRemoved();
}
/**
* @hide
*/
@@ -368,6 +444,12 @@ public class ImsConfigImplBase {
public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
}
/**
* The RCS autoconfiguration XML file is removed or invalid.
*/
public void notifyRcsAutoConfigurationRemoved() {
}
/**
* Sets the configuration value for this ImsService.
*
@@ -421,4 +503,43 @@ public class ImsConfigImplBase {
public void updateImsCarrierConfigs(PersistableBundle bundle) {
// Base Implementation - Should be overridden
}
/**
* Default messaging application parameters are sent to the ACS client
* using this interface.
* @param rcc RCS client configuration {@link RcsClientConfiguration}
*/
public void setRcsClientConfiguration(@NonNull RcsClientConfiguration rcc) {
// Base Implementation - Should be overridden
}
/**
* Reconfiguration triggered by the RCS application. Most likely cause
* is the 403 forbidden to a SIP/HTTP request
*/
public void triggerAutoConfiguration() {
// Base Implementation - Should be overridden
}
/**
* Errors during autoconfiguration connection setup are notified by the
* ACS client using this interface.
* @param errorCode HTTP error received during connection setup.
* @param errorString reason phrase received with the error
*/
public final void notifyAutoConfigurationErrorReceived(int errorCode,
@NonNull String errorString) {
// can be null in testing
if (mRcsCallbacks == null) {
return;
}
mRcsCallbacks.broadcastAction(c -> {
try {
//TODO compressed by default?
c.onAutoConfigurationErrorReceived(errorCode, errorString);
} catch (RemoteException e) {
Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping.");
}
});
}
}

View File

@@ -52,6 +52,7 @@ import android.telephony.SignalStrength;
import android.telephony.TelephonyHistogram;
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.RcsClientConfiguration;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
@@ -59,6 +60,7 @@ import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.aidl.IRcsConfigCallback;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IBooleanConsumer;
@@ -2303,4 +2305,51 @@ interface ITelephony {
* Return the release time for telephony to unbind GbaService.
*/
int getGbaReleaseTime(int subId);
/**
* Provide the client configuration parameters of the RCS application.
*/
void setRcsClientConfiguration(int subId, in RcsClientConfiguration rcc);
/**
* return value to indicate whether the device and the carrier can support RCS VoLTE
* single registration.
*/
boolean isRcsVolteSingleRegistrationCapable(int subId);
/**
* Register RCS provisioning callback.
*/
void registerRcsProvisioningChangedCallback(int subId,
IRcsConfigCallback callback);
/**
* Unregister RCS provisioning callback.
*/
void unregisterRcsProvisioningChangedCallback(int subId, IRcsConfigCallback callback);
/**
* trigger RCS reconfiguration.
*/
void triggerRcsReconfiguration(int subId);
/**
* Overrides the config of RCS VoLTE single registration enabled for the device.
*/
void setDeviceSingleRegistrationEnabledOverride(String enabled);
/**
* Gets the config of RCS VoLTE single registration enabled for the device.
*/
boolean getDeviceSingleRegistrationEnabled();
/**
* Overrides the config of RCS VoLTE single registration enabled for the carrier/subscription.
*/
boolean setCarrierSingleRegistrationEnabledOverride(int subId, String enabled);
/**
* Gets the config of RCS VoLTE single registration enabled for the carrier/subscription.
*/
boolean getCarrierSingleRegistrationEnabled(int subId);
}