Merge "Adding SimPhonebookContract" am: 110420fcae
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1521063 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: I3d3041404cacf8bd5a269253808efd898d538fe3
This commit is contained in:
@@ -34325,6 +34325,55 @@ package android.provider {
|
|||||||
field public static final String PATH_SETTING_INTENT = "intent";
|
field public static final String PATH_SETTING_INTENT = "intent";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class SimPhonebookContract {
|
||||||
|
field public static final String AUTHORITY = "com.android.simphonebook";
|
||||||
|
field @NonNull public static final android.net.Uri AUTHORITY_URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class SimPhonebookContract.ElementaryFiles {
|
||||||
|
method @NonNull public static android.net.Uri getItemUri(int, int);
|
||||||
|
field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-elementary-file";
|
||||||
|
field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
|
||||||
|
field @NonNull public static final android.net.Uri CONTENT_URI;
|
||||||
|
field public static final int EF_ADN = 1; // 0x1
|
||||||
|
field public static final int EF_FDN = 2; // 0x2
|
||||||
|
field public static final int EF_SDN = 3; // 0x3
|
||||||
|
field public static final String EF_TYPE = "ef_type";
|
||||||
|
field public static final int EF_UNKNOWN = 0; // 0x0
|
||||||
|
field public static final String MAX_RECORDS = "max_records";
|
||||||
|
field public static final String NAME_MAX_LENGTH = "name_max_length";
|
||||||
|
field public static final String PHONE_NUMBER_MAX_LENGTH = "phone_number_max_length";
|
||||||
|
field public static final String RECORD_COUNT = "record_count";
|
||||||
|
field public static final String SLOT_INDEX = "slot_index";
|
||||||
|
field public static final String SUBSCRIPTION_ID = "subscription_id";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class SimPhonebookContract.SimRecords {
|
||||||
|
method @NonNull public static android.net.Uri getContentUri(int, int);
|
||||||
|
method @NonNull public static android.net.Uri getItemUri(int, int, int);
|
||||||
|
method @NonNull @WorkerThread public static android.provider.SimPhonebookContract.SimRecords.NameValidationResult validateName(@NonNull android.content.ContentResolver, int, int, @NonNull String);
|
||||||
|
field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
|
||||||
|
field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
|
||||||
|
field public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
|
||||||
|
field public static final String NAME = "name";
|
||||||
|
field public static final String PHONE_NUMBER = "phone_number";
|
||||||
|
field public static final String RECORD_NUMBER = "record_number";
|
||||||
|
field public static final String SUBSCRIPTION_ID = "subscription_id";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class SimPhonebookContract.SimRecords.NameValidationResult implements android.os.Parcelable {
|
||||||
|
ctor public SimPhonebookContract.SimRecords.NameValidationResult(@NonNull String, @NonNull String, int, int);
|
||||||
|
method public int describeContents();
|
||||||
|
method public int getEncodedLength();
|
||||||
|
method public int getMaxEncodedLength();
|
||||||
|
method @NonNull public String getName();
|
||||||
|
method @NonNull public String getSanitizedName();
|
||||||
|
method public boolean isSupportedCharacter(int);
|
||||||
|
method public boolean isValid();
|
||||||
|
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||||
|
field @NonNull public static final android.os.Parcelable.Creator<android.provider.SimPhonebookContract.SimRecords.NameValidationResult> CREATOR;
|
||||||
|
}
|
||||||
|
|
||||||
public class SyncStateContract {
|
public class SyncStateContract {
|
||||||
ctor public SyncStateContract();
|
ctor public SyncStateContract();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8289,6 +8289,24 @@ package android.provider {
|
|||||||
method @RequiresPermission(android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, boolean);
|
method @RequiresPermission(android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class SimPhonebookContract {
|
||||||
|
method @NonNull public static String getEfUriPath(int);
|
||||||
|
field public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class SimPhonebookContract.ElementaryFiles {
|
||||||
|
field public static final String EF_ADN_PATH_SEGMENT = "adn";
|
||||||
|
field public static final String EF_FDN_PATH_SEGMENT = "fdn";
|
||||||
|
field public static final String EF_SDN_PATH_SEGMENT = "sdn";
|
||||||
|
field public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class SimPhonebookContract.SimRecords {
|
||||||
|
field public static final String EXTRA_NAME_VALIDATION_RESULT = "android.provider.extra.NAME_VALIDATION_RESULT";
|
||||||
|
field public static final String QUERY_ARG_PIN2 = "android:query-arg-pin2";
|
||||||
|
field public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
|
||||||
|
}
|
||||||
|
|
||||||
public static final class Telephony.Carriers implements android.provider.BaseColumns {
|
public static final class Telephony.Carriers implements android.provider.BaseColumns {
|
||||||
field public static final String APN_SET_ID = "apn_set_id";
|
field public static final String APN_SET_ID = "apn_set_id";
|
||||||
field public static final int CARRIER_EDITED = 4; // 0x4
|
field public static final int CARRIER_EDITED = 4; // 0x4
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
per-file *BlockedNumber* = file:/telephony/OWNERS
|
per-file *BlockedNumber* = file:/telephony/OWNERS
|
||||||
per-file *Telephony* = file:/telephony/OWNERS
|
per-file *Telephony* = file:/telephony/OWNERS
|
||||||
|
per-file *SimPhonebook* = file:/telephony/OWNERS
|
||||||
|
|
||||||
per-file *CallLog* = file:platform/packages/providers/ContactsProvider:/OWNERS
|
per-file *CallLog* = file:platform/packages/providers/ContactsProvider:/OWNERS
|
||||||
per-file *Contacts* = file:platform/packages/providers/ContactsProvider:/OWNERS
|
per-file *Contacts* = file:platform/packages/providers/ContactsProvider:/OWNERS
|
||||||
|
|||||||
506
core/java/android/provider/SimPhonebookContract.java
Normal file
506
core/java/android/provider/SimPhonebookContract.java
Normal file
@@ -0,0 +1,506 @@
|
|||||||
|
/*
|
||||||
|
* 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.provider;
|
||||||
|
|
||||||
|
import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN;
|
||||||
|
import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN_PATH_SEGMENT;
|
||||||
|
import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN;
|
||||||
|
import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN_PATH_SEGMENT;
|
||||||
|
import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN;
|
||||||
|
import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN_PATH_SEGMENT;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.SystemApi;
|
||||||
|
import android.annotation.WorkerThread;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.telephony.SubscriptionInfo;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
|
|
||||||
|
import com.android.internal.util.Preconditions;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The contract between the provider of contact records on the device's SIM cards and applications.
|
||||||
|
* Contains definitions of the supported URIs and columns.
|
||||||
|
*
|
||||||
|
* <p>This content provider does not support any of the QUERY_ARG_SQL* bundle arguments. An
|
||||||
|
* IllegalArgumentException will be thrown if these are included.
|
||||||
|
*/
|
||||||
|
public final class SimPhonebookContract {
|
||||||
|
|
||||||
|
/** The authority for the SIM phonebook provider. */
|
||||||
|
public static final String AUTHORITY = "com.android.simphonebook";
|
||||||
|
/** The content:// style uri to the authority for the SIM phonebook provider. */
|
||||||
|
@NonNull
|
||||||
|
public static final Uri AUTHORITY_URI = Uri.parse("content://com.android.simphonebook");
|
||||||
|
/**
|
||||||
|
* The Uri path element used to indicate that the following path segment is a subscription ID
|
||||||
|
* for the SIM card that will be operated on.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@SystemApi
|
||||||
|
public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
|
||||||
|
|
||||||
|
private SimPhonebookContract() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Uri path segment used to reference the specified elementary file type for Uris
|
||||||
|
* returned by this API.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
@SystemApi
|
||||||
|
public static String getEfUriPath(@ElementaryFiles.EfType int efType) {
|
||||||
|
switch (efType) {
|
||||||
|
case EF_ADN:
|
||||||
|
return EF_ADN_PATH_SEGMENT;
|
||||||
|
case EF_FDN:
|
||||||
|
return EF_FDN_PATH_SEGMENT;
|
||||||
|
case EF_SDN:
|
||||||
|
return EF_SDN_PATH_SEGMENT;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unsupported EfType " + efType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constants for the contact records on a SIM card. */
|
||||||
|
public static final class SimRecords {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The subscription ID of the SIM the record is from.
|
||||||
|
*
|
||||||
|
* @see SubscriptionInfo#getSubscriptionId()
|
||||||
|
*/
|
||||||
|
public static final String SUBSCRIPTION_ID = "subscription_id";
|
||||||
|
/**
|
||||||
|
* The type of the elementary file the record is from.
|
||||||
|
*
|
||||||
|
* @see ElementaryFiles#EF_ADN
|
||||||
|
* @see ElementaryFiles#EF_FDN
|
||||||
|
* @see ElementaryFiles#EF_SDN
|
||||||
|
*/
|
||||||
|
public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
|
||||||
|
/**
|
||||||
|
* The 1-based offset of the record in the elementary file that contains it.
|
||||||
|
*
|
||||||
|
* <p>This can be used to access individual SIM records by appending it to the
|
||||||
|
* elementary file URIs but it is not like a normal database ID because it is not
|
||||||
|
* auto-incrementing and it is not unique across SIM cards or elementary files. Hence, care
|
||||||
|
* should be taken when using it to ensure that it is applied to the correct SIM and EF.
|
||||||
|
*
|
||||||
|
* @see #getItemUri(int, int, int)
|
||||||
|
*/
|
||||||
|
public static final String RECORD_NUMBER = "record_number";
|
||||||
|
/**
|
||||||
|
* The name for this record.
|
||||||
|
*
|
||||||
|
* <p>An {@link IllegalArgumentException} will be thrown by insert and update if this
|
||||||
|
* exceeds the maximum supported length or contains unsupported characters.
|
||||||
|
* {@link #validateName(ContentResolver, int, int, String)} )} can be used to
|
||||||
|
* check whether the name is supported.
|
||||||
|
*
|
||||||
|
* @see ElementaryFiles#NAME_MAX_LENGTH
|
||||||
|
* @see #validateName(ContentResolver, int, int, String) )
|
||||||
|
*/
|
||||||
|
public static final String NAME = "name";
|
||||||
|
/**
|
||||||
|
* The phone number for this record.
|
||||||
|
*
|
||||||
|
* <p>Only dialable characters are supported.
|
||||||
|
*
|
||||||
|
* <p>An {@link IllegalArgumentException} will be thrown by insert and update if this
|
||||||
|
* exceeds the maximum supported length or contains unsupported characters.
|
||||||
|
*
|
||||||
|
* @see ElementaryFiles#PHONE_NUMBER_MAX_LENGTH
|
||||||
|
* @see android.telephony.PhoneNumberUtils#isDialable(char)
|
||||||
|
*/
|
||||||
|
public static final String PHONE_NUMBER = "phone_number";
|
||||||
|
|
||||||
|
/** The MIME type of a CONTENT_URI subdirectory of a single SIM record. */
|
||||||
|
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
|
||||||
|
/** The MIME type of CONTENT_URI providing a directory of SIM records. */
|
||||||
|
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path segment that is appended to {@link #getContentUri(int, int)} which indicates
|
||||||
|
* that the following path segment contains a name to be validated.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
* @see #validateName(ContentResolver, int, int, String)
|
||||||
|
*/
|
||||||
|
@SystemApi
|
||||||
|
public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key for a cursor extra that contains the result of a validate name query.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
* @see #validateName(ContentResolver, int, int, String)
|
||||||
|
*/
|
||||||
|
@SystemApi
|
||||||
|
public static final String EXTRA_NAME_VALIDATION_RESULT =
|
||||||
|
"android.provider.extra.NAME_VALIDATION_RESULT";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key for the PIN2 needed to modify FDN record that should be passed in the Bundle
|
||||||
|
* passed to {@link ContentResolver#insert(Uri, ContentValues, Bundle)},
|
||||||
|
* {@link ContentResolver#update(Uri, ContentValues, Bundle)}
|
||||||
|
* and {@link ContentResolver#delete(Uri, Bundle)}.
|
||||||
|
*
|
||||||
|
* <p>Modifying FDN records also requires either
|
||||||
|
* {@link android.Manifest.permission#MODIFY_PHONE_STATE} or
|
||||||
|
* {@link TelephonyManager#hasCarrierPrivileges()}
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@SystemApi
|
||||||
|
public static final String QUERY_ARG_PIN2 = "android:query-arg-pin2";
|
||||||
|
|
||||||
|
private SimRecords() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content Uri for the specified elementary file on the specified SIM.
|
||||||
|
*
|
||||||
|
* <p>When queried this Uri will return all of the contact records in the specified
|
||||||
|
* elementary file on the specified SIM. The available subscriptionIds and efTypes can
|
||||||
|
* be discovered by querying {@link ElementaryFiles#CONTENT_URI}.
|
||||||
|
*
|
||||||
|
* <p>If a SIM with the provided subscription ID does not exist or the SIM with the provided
|
||||||
|
* subscription ID doesn't support the specified entity file then queries will return
|
||||||
|
* and empty cursor and inserts will throw an {@link IllegalArgumentException}
|
||||||
|
*
|
||||||
|
* @param subscriptionId the subscriptionId of the SIM card that this Uri will reference
|
||||||
|
* @param efType the elementary file on the SIM that this Uri will reference
|
||||||
|
* @see ElementaryFiles#EF_ADN
|
||||||
|
* @see ElementaryFiles#EF_FDN
|
||||||
|
* @see ElementaryFiles#EF_SDN
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static Uri getContentUri(int subscriptionId, @ElementaryFiles.EfType int efType) {
|
||||||
|
return buildContentUri(subscriptionId, efType).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content Uri for the specific SIM record with the provided {@link #RECORD_NUMBER}.
|
||||||
|
*
|
||||||
|
* <p>When queried this will return the record identified by the provided arguments.
|
||||||
|
*
|
||||||
|
* <p>For a non-existent record:
|
||||||
|
* <ul>
|
||||||
|
* <li>query will return an empty cursor</li>
|
||||||
|
* <li>update will return 0</li>
|
||||||
|
* <li>delete will return 0</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param subscriptionId the subscription ID of the SIM containing the record. If no SIM
|
||||||
|
* with this subscription ID exists then it will be treated as a
|
||||||
|
* non-existent record
|
||||||
|
* @param efType the elementary file type containing the record. If the specified
|
||||||
|
* SIM doesn't support this elementary file then it will be treated
|
||||||
|
* as a non-existent record.
|
||||||
|
* @param recordNumber the record number of the record this Uri should reference. This
|
||||||
|
* must be greater than 0. If there is no record with this record
|
||||||
|
* number in the specified entity file then it will be treated as a
|
||||||
|
* non-existent record.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static Uri getItemUri(
|
||||||
|
int subscriptionId, @ElementaryFiles.EfType int efType, int recordNumber) {
|
||||||
|
// Elementary file record indices are 1-based.
|
||||||
|
Preconditions.checkArgument(recordNumber > 0, "Invalid recordNumber");
|
||||||
|
|
||||||
|
return buildContentUri(subscriptionId, efType)
|
||||||
|
.appendPath(String.valueOf(recordNumber))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates a value that is being provided for the {@link #NAME} column.
|
||||||
|
*
|
||||||
|
* <p>The return value can be used to check if the name is valid. If it is not valid then
|
||||||
|
* inserts and updates to the specified elementary file that use the provided name value
|
||||||
|
* will throw an {@link IllegalArgumentException}.
|
||||||
|
*
|
||||||
|
* <p>If the specified SIM or elementary file don't exist then
|
||||||
|
* {@link NameValidationResult#getMaxEncodedLength()} will be zero and
|
||||||
|
* {@link NameValidationResult#isValid()} will return false.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
@WorkerThread
|
||||||
|
public static NameValidationResult validateName(
|
||||||
|
@NonNull ContentResolver resolver, int subscriptionId,
|
||||||
|
@ElementaryFiles.EfType int efType,
|
||||||
|
@NonNull String name) {
|
||||||
|
Bundle queryArgs = new Bundle();
|
||||||
|
queryArgs.putString(SimRecords.NAME, name);
|
||||||
|
try (Cursor cursor =
|
||||||
|
resolver.query(buildContentUri(subscriptionId, efType)
|
||||||
|
.appendPath(VALIDATE_NAME_PATH_SEGMENT)
|
||||||
|
.build(), null, queryArgs, null)) {
|
||||||
|
NameValidationResult result = cursor.getExtras()
|
||||||
|
.getParcelable(EXTRA_NAME_VALIDATION_RESULT);
|
||||||
|
return result != null ? result : new NameValidationResult(name, "", 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Uri.Builder buildContentUri(
|
||||||
|
int subscriptionId, @ElementaryFiles.EfType int efType) {
|
||||||
|
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
|
||||||
|
.authority(AUTHORITY)
|
||||||
|
.appendPath(SUBSCRIPTION_ID_PATH_SEGMENT)
|
||||||
|
.appendPath(String.valueOf(subscriptionId))
|
||||||
|
.appendPath(getEfUriPath(efType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Contains details about the validity of a value provided for the {@link #NAME} column. */
|
||||||
|
public static final class NameValidationResult implements Parcelable {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static final Creator<NameValidationResult> CREATOR =
|
||||||
|
new Creator<NameValidationResult>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NameValidationResult createFromParcel(@NonNull Parcel in) {
|
||||||
|
return new NameValidationResult(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public NameValidationResult[] newArray(int size) {
|
||||||
|
return new NameValidationResult[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final String mName;
|
||||||
|
private final String mSanitizedName;
|
||||||
|
private final int mEncodedLength;
|
||||||
|
private final int mMaxEncodedLength;
|
||||||
|
|
||||||
|
/** Creates a new instance from the provided values. */
|
||||||
|
public NameValidationResult(@NonNull String name, @NonNull String sanitizedName,
|
||||||
|
int encodedLength, int maxEncodedLength) {
|
||||||
|
this.mName = Objects.requireNonNull(name);
|
||||||
|
this.mSanitizedName = Objects.requireNonNull(sanitizedName);
|
||||||
|
this.mEncodedLength = encodedLength;
|
||||||
|
this.mMaxEncodedLength = maxEncodedLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NameValidationResult(Parcel in) {
|
||||||
|
this(in.readString(), in.readString(), in.readInt(), in.readInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the original name that is being validated. */
|
||||||
|
@NonNull
|
||||||
|
public String getName() {
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a sanitized copy of the original name with all unsupported characters
|
||||||
|
* replaced with spaces.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public String getSanitizedName() {
|
||||||
|
return mSanitizedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the original name isValid.
|
||||||
|
*
|
||||||
|
* <p>If this returns false then inserts and updates using the name will throw an
|
||||||
|
* {@link IllegalArgumentException}
|
||||||
|
*/
|
||||||
|
public boolean isValid() {
|
||||||
|
return mMaxEncodedLength > 0 && mEncodedLength <= mMaxEncodedLength
|
||||||
|
&& Objects.equals(
|
||||||
|
mName, mSanitizedName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns whether the character at the specified position is supported by the SIM. */
|
||||||
|
public boolean isSupportedCharacter(int position) {
|
||||||
|
return mName.charAt(position) == mSanitizedName.charAt(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of bytes required to save the name.
|
||||||
|
*
|
||||||
|
* <p>This may be more than the number of characters in the name.
|
||||||
|
*/
|
||||||
|
public int getEncodedLength() {
|
||||||
|
return mEncodedLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum number of bytes that are supported for the name.
|
||||||
|
*
|
||||||
|
* @see ElementaryFiles#NAME_MAX_LENGTH
|
||||||
|
*/
|
||||||
|
public int getMaxEncodedLength() {
|
||||||
|
return mMaxEncodedLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||||
|
dest.writeString(mName);
|
||||||
|
dest.writeString(mSanitizedName);
|
||||||
|
dest.writeInt(mEncodedLength);
|
||||||
|
dest.writeInt(mMaxEncodedLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constants for metadata about the elementary files of the SIM cards in the phone. */
|
||||||
|
public static final class ElementaryFiles {
|
||||||
|
|
||||||
|
/** {@link SubscriptionInfo#getSimSlotIndex()} of the SIM for this row. */
|
||||||
|
public static final String SLOT_INDEX = "slot_index";
|
||||||
|
/** {@link SubscriptionInfo#getSubscriptionId()} of the SIM for this row. */
|
||||||
|
public static final String SUBSCRIPTION_ID = "subscription_id";
|
||||||
|
/**
|
||||||
|
* The elementary file type for this row.
|
||||||
|
*
|
||||||
|
* @see ElementaryFiles#EF_ADN
|
||||||
|
* @see ElementaryFiles#EF_FDN
|
||||||
|
* @see ElementaryFiles#EF_SDN
|
||||||
|
*/
|
||||||
|
public static final String EF_TYPE = "ef_type";
|
||||||
|
/** The maximum number of records supported by the elementary file. */
|
||||||
|
public static final String MAX_RECORDS = "max_records";
|
||||||
|
/** Count of the number of records that are currently stored in the elementary file. */
|
||||||
|
public static final String RECORD_COUNT = "record_count";
|
||||||
|
/** The maximum length supported for the name of a record in the elementary file. */
|
||||||
|
public static final String NAME_MAX_LENGTH = "name_max_length";
|
||||||
|
/**
|
||||||
|
* The maximum length supported for the phone number of a record in the elementary file.
|
||||||
|
*/
|
||||||
|
public static final String PHONE_NUMBER_MAX_LENGTH = "phone_number_max_length";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A value for an elementary file that is not recognized.
|
||||||
|
*
|
||||||
|
* <p>Generally this should be ignored. If new values are added then this will be used
|
||||||
|
* for apps that target SDKs where they aren't defined.
|
||||||
|
*/
|
||||||
|
public static final int EF_UNKNOWN = 0;
|
||||||
|
/**
|
||||||
|
* Type for accessing records in the "abbreviated dialing number" (ADN) elementary file on
|
||||||
|
* the SIM.
|
||||||
|
*
|
||||||
|
* <p>ADN records are typically user created.
|
||||||
|
*/
|
||||||
|
public static final int EF_ADN = 1;
|
||||||
|
/**
|
||||||
|
* Type for accessing records in the "fixed dialing number" (FDN) elementary file on the
|
||||||
|
* SIM.
|
||||||
|
*
|
||||||
|
* <p>FDN numbers are the numbers that are allowed to dialed for outbound calls when FDN is
|
||||||
|
* enabled.
|
||||||
|
*
|
||||||
|
* <p>FDN records cannot be modified by applications. Hence, insert, update and
|
||||||
|
* delete methods operating on this Uri will throw UnsupportedOperationException
|
||||||
|
*/
|
||||||
|
public static final int EF_FDN = 2;
|
||||||
|
/**
|
||||||
|
* Type for accessing records in the "service dialing number" (SDN) elementary file on the
|
||||||
|
* SIM.
|
||||||
|
*
|
||||||
|
* <p>Typically SDNs are preset numbers provided by the carrier for common operations (e.g.
|
||||||
|
* voicemail, check balance, etc).
|
||||||
|
*
|
||||||
|
* <p>SDN records cannot be modified by applications. Hence, insert, update and delete
|
||||||
|
* methods operating on this Uri will throw UnsupportedOperationException
|
||||||
|
*/
|
||||||
|
public static final int EF_SDN = 3;
|
||||||
|
/** @hide */
|
||||||
|
@SystemApi
|
||||||
|
public static final String EF_ADN_PATH_SEGMENT = "adn";
|
||||||
|
/** @hide */
|
||||||
|
@SystemApi
|
||||||
|
public static final String EF_FDN_PATH_SEGMENT = "fdn";
|
||||||
|
/** @hide */
|
||||||
|
@SystemApi
|
||||||
|
public static final String EF_SDN_PATH_SEGMENT = "sdn";
|
||||||
|
/** The MIME type of CONTENT_URI providing a directory of ADN-like elementary files. */
|
||||||
|
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
|
||||||
|
/** The MIME type of a CONTENT_URI subdirectory of a single ADN-like elementary file. */
|
||||||
|
public static final String CONTENT_ITEM_TYPE =
|
||||||
|
"vnd.android.cursor.item/sim-elementary-file";
|
||||||
|
/**
|
||||||
|
* The Uri path segment used to construct Uris for the metadata defined in this class.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@SystemApi
|
||||||
|
public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
|
||||||
|
|
||||||
|
/** Content URI for the ADN-like elementary files available on the device. */
|
||||||
|
@NonNull
|
||||||
|
public static final Uri CONTENT_URI = AUTHORITY_URI
|
||||||
|
.buildUpon()
|
||||||
|
.appendPath(ELEMENTARY_FILES_PATH_SEGMENT).build();
|
||||||
|
|
||||||
|
private ElementaryFiles() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a content uri for a specific elementary file.
|
||||||
|
*
|
||||||
|
* <p>If a SIM with the specified subscriptionId is not present an exception will be thrown.
|
||||||
|
* If the SIM doesn't support the specified elementary file it will have a zero value for
|
||||||
|
* {@link #MAX_RECORDS}.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static Uri getItemUri(int subscriptionId, @EfType int efType) {
|
||||||
|
return CONTENT_URI.buildUpon().appendPath(SUBSCRIPTION_ID_PATH_SEGMENT)
|
||||||
|
.appendPath(String.valueOf(subscriptionId))
|
||||||
|
.appendPath(getEfUriPath(efType))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation for the valid elementary file types.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef(
|
||||||
|
prefix = {"EF"},
|
||||||
|
value = {EF_UNKNOWN, EF_ADN, EF_FDN, EF_SDN})
|
||||||
|
public @interface EfType {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* 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.provider;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.testng.Assert.assertThrows;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.provider.SimPhonebookContract.SimRecords.NameValidationResult;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class SimPhonebookContractTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getContentUri_invalidEfType_throwsIllegalArgumentException() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
SimPhonebookContract.SimRecords.getContentUri(1, 100)
|
||||||
|
);
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
SimPhonebookContract.SimRecords.getContentUri(1, -1)
|
||||||
|
);
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
SimPhonebookContract.SimRecords.getContentUri(1,
|
||||||
|
SimPhonebookContract.ElementaryFiles.EF_UNKNOWN)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getItemUri_invalidEfType_throwsIllegalArgumentException() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
SimPhonebookContract.SimRecords.getItemUri(1, 100, 1)
|
||||||
|
);
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
SimPhonebookContract.SimRecords.getItemUri(1, -1, 1)
|
||||||
|
);
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
SimPhonebookContract.SimRecords.getItemUri(1,
|
||||||
|
SimPhonebookContract.ElementaryFiles.EF_UNKNOWN, 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getItemUri_invalidRecordIndex_throwsIllegalArgumentException() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
SimPhonebookContract.SimRecords.getItemUri(1,
|
||||||
|
SimPhonebookContract.ElementaryFiles.EF_ADN, 0)
|
||||||
|
);
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
SimPhonebookContract.SimRecords.getItemUri(1,
|
||||||
|
SimPhonebookContract.ElementaryFiles.EF_ADN, -1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nameValidationResult_isValid_validNames() {
|
||||||
|
assertThat(new NameValidationResult("", "", 0, 1).isValid()).isTrue();
|
||||||
|
assertThat(new NameValidationResult("a", "a", 1, 1).isValid()).isTrue();
|
||||||
|
assertThat(new NameValidationResult("First Last", "First Last", 10, 10).isValid()).isTrue();
|
||||||
|
assertThat(
|
||||||
|
new NameValidationResult("First Last", "First Last", 10, 100).isValid()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nameValidationResult_isValid_invalidNames() {
|
||||||
|
assertThat(new NameValidationResult("", "", 0, 0).isValid()).isFalse();
|
||||||
|
assertThat(new NameValidationResult("ab", "ab", 2, 1).isValid()).isFalse();
|
||||||
|
NameValidationResult unsupportedCharactersResult = new NameValidationResult("A_b_c",
|
||||||
|
"A b c", 5, 5);
|
||||||
|
assertThat(unsupportedCharactersResult.isValid()).isFalse();
|
||||||
|
assertThat(unsupportedCharactersResult.isSupportedCharacter(0)).isTrue();
|
||||||
|
assertThat(unsupportedCharactersResult.isSupportedCharacter(1)).isFalse();
|
||||||
|
assertThat(unsupportedCharactersResult.isSupportedCharacter(2)).isTrue();
|
||||||
|
assertThat(unsupportedCharactersResult.isSupportedCharacter(3)).isFalse();
|
||||||
|
assertThat(unsupportedCharactersResult.isSupportedCharacter(4)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nameValidationResult_parcel() {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put("name", "Name");
|
||||||
|
values.put("phone_number", "123");
|
||||||
|
|
||||||
|
NameValidationResult result;
|
||||||
|
Parcel parcel = Parcel.obtain();
|
||||||
|
try {
|
||||||
|
parcel.writeParcelable(new NameValidationResult("name", "sanitized name", 1, 2), 0);
|
||||||
|
parcel.setDataPosition(0);
|
||||||
|
result = parcel.readParcelable(NameValidationResult.class.getClassLoader());
|
||||||
|
} finally {
|
||||||
|
parcel.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(result.getName()).isEqualTo("name");
|
||||||
|
assertThat(result.getSanitizedName()).isEqualTo("sanitized name");
|
||||||
|
assertThat(result.getEncodedLength()).isEqualTo(1);
|
||||||
|
assertThat(result.getMaxEncodedLength()).isEqualTo(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user