blob: 8ab772244d7677aaf40f9ed5726ac7e1f93b79d5 [file] [log] [blame]
/*
* Copyright (C) 2006 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 com.android.internal.telephony.uicc;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import com.android.internal.telephony.GsmAlphabet;
import com.android.telephony.Rlog;
import java.util.Arrays;
/**
*
* Used to load or store ADNs (Abbreviated Dialing Numbers).
*
* {@hide}
*
*/
public class AdnRecord implements Parcelable {
static final String LOG_TAG = "AdnRecord";
//***** Instance Variables
@UnsupportedAppUsage
String mAlphaTag = null;
@UnsupportedAppUsage
String mNumber = null;
@UnsupportedAppUsage
String[] mEmails;
@UnsupportedAppUsage
int mExtRecord = 0xff;
@UnsupportedAppUsage
int mEfid; // or 0 if none
@UnsupportedAppUsage
int mRecordNumber; // or 0 if none
//***** Constants
// In an ADN record, everything but the alpha identifier
// is in a footer that's 14 bytes
static final int FOOTER_SIZE_BYTES = 14;
// Maximum size of the un-extended number field
static final int MAX_NUMBER_SIZE_BYTES = 11;
static final int EXT_RECORD_LENGTH_BYTES = 13;
static final int EXT_RECORD_TYPE_ADDITIONAL_DATA = 2;
static final int EXT_RECORD_TYPE_MASK = 3;
static final int MAX_EXT_CALLED_PARTY_LENGTH = 0xa;
// ADN offset
static final int ADN_BCD_NUMBER_LENGTH = 0;
static final int ADN_TON_AND_NPI = 1;
static final int ADN_DIALING_NUMBER_START = 2;
static final int ADN_DIALING_NUMBER_END = 11;
static final int ADN_CAPABILITY_ID = 12;
static final int ADN_EXTENSION_ID = 13;
//***** Static Methods
@UnsupportedAppUsage
public static final Parcelable.Creator<AdnRecord> CREATOR
= new Parcelable.Creator<AdnRecord>() {
@Override
public AdnRecord createFromParcel(Parcel source) {
int efid;
int recordNumber;
String alphaTag;
String number;
String[] emails;
efid = source.readInt();
recordNumber = source.readInt();
alphaTag = source.readString();
number = source.readString();
final int len = source.readInt();
if (len > 0) {
emails = new String[len];
source.readStringArray(emails);
return new AdnRecord(efid, recordNumber, alphaTag, number, emails);
} else {
return new AdnRecord(efid, recordNumber, alphaTag, number, null);
}
}
@Override
public AdnRecord[] newArray(int size) {
return new AdnRecord[size];
}
};
//***** Constructor
@UnsupportedAppUsage
public AdnRecord (byte[] record) {
this(0, 0, record);
}
@UnsupportedAppUsage
public AdnRecord (int efid, int recordNumber, byte[] record) {
this.mEfid = efid;
this.mRecordNumber = recordNumber;
parseRecord(record);
}
@UnsupportedAppUsage
public AdnRecord (String alphaTag, String number) {
this(0, 0, alphaTag, number);
}
@UnsupportedAppUsage
public AdnRecord (String alphaTag, String number, String[] emails) {
this(0, 0, alphaTag, number, emails);
}
@UnsupportedAppUsage
public AdnRecord (int efid, int recordNumber, String alphaTag, String number, String[] emails) {
this.mEfid = efid;
this.mRecordNumber = recordNumber;
this.mAlphaTag = alphaTag;
this.mNumber = number;
this.mEmails = emails;
}
@UnsupportedAppUsage
public AdnRecord(int efid, int recordNumber, String alphaTag, String number) {
this.mEfid = efid;
this.mRecordNumber = recordNumber;
this.mAlphaTag = alphaTag;
this.mNumber = number;
this.mEmails = null;
}
//***** Instance Methods
public String getAlphaTag() {
return mAlphaTag;
}
public int getEfid() {
return mEfid;
}
public int getRecId() {
return mRecordNumber;
}
@UnsupportedAppUsage
public String getNumber() {
return mNumber;
}
public void setNumber(String number) {
mNumber = number;
}
@UnsupportedAppUsage
public String[] getEmails() {
return mEmails;
}
@UnsupportedAppUsage
public void setEmails(String[] emails) {
this.mEmails = emails;
}
@Override
public String toString() {
return "ADN Record '" + mAlphaTag + "' '" + Rlog.pii(LOG_TAG, mNumber) + " "
+ Rlog.pii(LOG_TAG, mEmails) + "'";
}
@UnsupportedAppUsage
public boolean isEmpty() {
return TextUtils.isEmpty(mAlphaTag) && TextUtils.isEmpty(mNumber) && mEmails == null;
}
public boolean hasExtendedRecord() {
return mExtRecord != 0 && mExtRecord != 0xff;
}
/** Helper function for {@link #isEqual}. */
private static boolean stringCompareNullEqualsEmpty(String s1, String s2) {
if (s1 == s2) {
return true;
}
if (s1 == null) {
s1 = "";
}
if (s2 == null) {
s2 = "";
}
return (s1.equals(s2));
}
public boolean isEqual(AdnRecord adn) {
return ( stringCompareNullEqualsEmpty(mAlphaTag, adn.mAlphaTag) &&
stringCompareNullEqualsEmpty(mNumber, adn.mNumber) &&
Arrays.equals(mEmails, adn.mEmails));
}
//***** Parcelable Implementation
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mEfid);
dest.writeInt(mRecordNumber);
dest.writeString(mAlphaTag);
dest.writeString(mNumber);
dest.writeStringArray(mEmails);
}
/**
* Build adn hex byte array based on record size
* The format of byte array is defined in 51.011 10.5.1
*
* @param recordSize is the size X of EF record
* @return hex byte[recordSize] to be written to EF record
* return null for wrong format of dialing number or tag
*/
@UnsupportedAppUsage
public byte[] buildAdnString(int recordSize) {
byte[] bcdNumber;
byte[] byteTag;
byte[] adnString;
int footerOffset = recordSize - FOOTER_SIZE_BYTES;
// create an empty record
adnString = new byte[recordSize];
for (int i = 0; i < recordSize; i++) {
adnString[i] = (byte) 0xFF;
}
if (TextUtils.isEmpty(mNumber)) {
Rlog.w(LOG_TAG, "[buildAdnString] Empty dialing number");
return adnString; // return the empty record (for delete)
} else if (mNumber.length()
> (ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1) * 2) {
Rlog.w(LOG_TAG,
"[buildAdnString] Max length of dialing number is 20");
return null;
}
byteTag = !TextUtils.isEmpty(mAlphaTag) ? GsmAlphabet.stringToGsm8BitPacked(mAlphaTag)
: new byte[0];
if (byteTag.length > footerOffset) {
Rlog.w(LOG_TAG, "[buildAdnString] Max length of tag is " + footerOffset);
return null;
} else {
bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
mNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
System.arraycopy(bcdNumber, 0, adnString,
footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
adnString[footerOffset + ADN_BCD_NUMBER_LENGTH]
= (byte) (bcdNumber.length);
adnString[footerOffset + ADN_CAPABILITY_ID]
= (byte) 0xFF; // Capability Id
adnString[footerOffset + ADN_EXTENSION_ID]
= (byte) 0xFF; // Extension Record Id
if (byteTag.length > 0) {
System.arraycopy(byteTag, 0, adnString, 0, byteTag.length);
}
return adnString;
}
}
/**
* See TS 51.011 10.5.10
*/
public void
appendExtRecord (byte[] extRecord) {
try {
if (extRecord.length != EXT_RECORD_LENGTH_BYTES) {
return;
}
if ((extRecord[0] & EXT_RECORD_TYPE_MASK)
!= EXT_RECORD_TYPE_ADDITIONAL_DATA) {
return;
}
if ((0xff & extRecord[1]) > MAX_EXT_CALLED_PARTY_LENGTH) {
// invalid or empty record
return;
}
mNumber += PhoneNumberUtils.calledPartyBCDFragmentToString(
extRecord,
2,
0xff & extRecord[1],
PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
// We don't support ext record chaining.
} catch (RuntimeException ex) {
Rlog.w(LOG_TAG, "Error parsing AdnRecord ext record", ex);
}
}
//***** Private Methods
/**
* alphaTag and number are set to null on invalid format
*/
private void
parseRecord(byte[] record) {
try {
mAlphaTag = IccUtils.adnStringFieldToString(
record, 0, record.length - FOOTER_SIZE_BYTES);
int footerOffset = record.length - FOOTER_SIZE_BYTES;
int numberLength = 0xff & record[footerOffset];
if (numberLength > MAX_NUMBER_SIZE_BYTES) {
// Invalid number length
mNumber = "";
return;
}
// Please note 51.011 10.5.1:
//
// "If the Dialling Number/SSC String does not contain
// a dialling number, e.g. a control string deactivating
// a service, the TON/NPI byte shall be set to 'FF' by
// the ME (see note 2)."
mNumber = PhoneNumberUtils.calledPartyBCDToString(
record,
footerOffset + 1,
numberLength,
PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
mExtRecord = 0xff & record[record.length - 1];
mEmails = null;
} catch (RuntimeException ex) {
Rlog.w(LOG_TAG, "Error parsing AdnRecord", ex);
mNumber = "";
mAlphaTag = "";
mEmails = null;
}
}
}