Merge "Do not send update to carrier config if SIM card is still locked" into sc-dev
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 362b128..02530da 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -29,6 +29,8 @@
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
+import com.android.internal.telephony.uicc.SimPhonebookRecord;
+
import java.util.ArrayList;
import java.util.List;
@@ -109,6 +111,8 @@
protected RegistrantList mEmergencyNumberListRegistrants = new RegistrantList();
protected RegistrantList mUiccApplicationsEnablementRegistrants = new RegistrantList();
protected RegistrantList mBarringInfoChangedRegistrants = new RegistrantList();
+ protected RegistrantList mSimPhonebookChangedRegistrants = new RegistrantList();
+ protected RegistrantList mSimPhonebookRecordsReceivedRegistrants = new RegistrantList();
@UnsupportedAppUsage
protected Registrant mGsmSmsRegistrant;
@@ -1085,4 +1089,36 @@
public void unregisterForBarringInfoChanged(Handler h) {
mBarringInfoChangedRegistrants.remove(h);
}
+
+ @Override
+ public void registerForSimPhonebookChanged(Handler h, int what, Object obj) {
+ mSimPhonebookChangedRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForSimPhonebookChanged(Handler h) {
+ mSimPhonebookChangedRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForSimPhonebookRecordsReceived(Handler h, int what, Object obj) {
+ mSimPhonebookRecordsReceivedRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForSimPhonebookRecordsReceived(Handler h) {
+ mSimPhonebookRecordsReceivedRegistrants.remove(h);
+ }
+
+ @Override
+ public void getSimPhonebookRecords(Message result) {
+ }
+
+ @Override
+ public void getSimPhonebookCapacity(Message result) {
+ }
+
+ @Override
+ public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) {
+ }
}
diff --git a/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
index 9afb0fa..6bc2450 100644
--- a/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
+++ b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
@@ -102,7 +102,8 @@
}
/**
- * @return {@code true} if the SMS was handled by carrier services.
+ * @return {@code true} if the SMS was handled by a carrier application or an ImsService
+ * implementing RCS features.
*/
@VisibleForTesting
public boolean filter() {
@@ -111,10 +112,10 @@
if (carrierAppForFiltering.isPresent()) {
smsFilterPackages.add(carrierAppForFiltering.get());
}
- String carrierImsPackage = CarrierSmsUtils.getCarrierImsPackageForIntent(mContext, mPhone,
+ String imsRcsPackage = CarrierSmsUtils.getImsRcsPackageForIntent(mContext, mPhone,
new Intent(CarrierMessagingService.SERVICE_INTERFACE));
- if (carrierImsPackage != null) {
- smsFilterPackages.add(carrierImsPackage);
+ if (imsRcsPackage != null) {
+ smsFilterPackages.add(imsRcsPackage);
}
if (mFilterAggregator != null) {
diff --git a/src/java/com/android/internal/telephony/CarrierSmsUtils.java b/src/java/com/android/internal/telephony/CarrierSmsUtils.java
index f78d147..76a0c23 100644
--- a/src/java/com/android/internal/telephony/CarrierSmsUtils.java
+++ b/src/java/com/android/internal/telephony/CarrierSmsUtils.java
@@ -22,9 +22,9 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
+import android.telephony.ims.feature.ImsFeature;
+import com.android.internal.telephony.ims.ImsResolver;
import com.android.telephony.Rlog;
import java.util.List;
@@ -36,23 +36,20 @@
protected static final boolean VDBG = false;
protected static final String TAG = CarrierSmsUtils.class.getSimpleName();
- private static final String CARRIER_IMS_PACKAGE_KEY =
- CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING;
-
- /** Return a Carrier-overridden IMS package, if it exists and is a CarrierSmsFilter
- *
+ /**
+ * Return the package name of the ImsService that is implementing RCS features for the device.
* @param context calling context
* @param phone object from telephony
* @param intent that should match a CarrierSmsFilter
- * @return the name of the IMS CarrierService package
+ * @return the name of the ImsService implementing RCS features on the device.
*/
@Nullable
- public static String getCarrierImsPackageForIntent(
+ public static String getImsRcsPackageForIntent(
Context context, Phone phone, Intent intent) {
- String carrierImsPackage = getCarrierImsPackage(context, phone);
+ String carrierImsPackage = getImsRcsPackage(phone);
if (carrierImsPackage == null) {
- if (VDBG) Rlog.v(TAG, "No CarrierImsPackage override found");
+ if (VDBG) Rlog.v(TAG, "No ImsService found implementing RCS.");
return null;
}
@@ -71,23 +68,22 @@
return null;
}
+ /**
+ * @return the package name of the ImsService that is configured to implement RCS, or null if
+ * there is none configured/available.
+ */
@Nullable
- private static String getCarrierImsPackage(Context context, Phone phone) {
- CarrierConfigManager cm = (CarrierConfigManager) context.getSystemService(
- Context.CARRIER_CONFIG_SERVICE);
- if (cm == null) {
- Rlog.e(TAG, "Failed to retrieve CarrierConfigManager");
+ private static String getImsRcsPackage(Phone phone) {
+ ImsResolver resolver = ImsResolver.getInstance();
+ if (resolver == null) {
+ Rlog.i(TAG, "getImsRcsPackage: Device does not support IMS - skipping");
return null;
}
final long identity = Binder.clearCallingIdentity();
try {
- PersistableBundle config = cm.getConfigForSubId(phone.getSubId());
- if (config == null) {
- if (VDBG) Rlog.v(TAG, "No CarrierConfig for subId:" + phone.getSubId());
- return null;
- }
- return config.getString(CARRIER_IMS_PACKAGE_KEY, null);
+ return resolver.getConfiguredImsServicePackageName(phone.getPhoneId(),
+ ImsFeature.FEATURE_RCS);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java b/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java
index 7045171..30d6129 100644
--- a/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java
+++ b/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java
@@ -325,6 +325,17 @@
pw.println("CellBroadcastServiceManager:");
pw.println(" mEnabled=" + mEnabled);
pw.println(" mCellBroadcastServicePackage=" + mCellBroadcastServicePackage);
+ if (mEnabled) {
+ try {
+ if (sServiceConnection != null && sServiceConnection.mService != null) {
+ sServiceConnection.mService.dump(fd, args);
+ } else {
+ pw.println(" sServiceConnection is null");
+ }
+ } catch (RemoteException e) {
+ pw.println(" mService.dump() threw RemoteException e: " + e.toString());
+ }
+ }
mLocalLog.dump(fd, pw, args);
pw.flush();
}
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 6ae0b1f..a6ff3fa 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -42,6 +42,7 @@
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import com.android.internal.telephony.uicc.IccCardStatus;
+import com.android.internal.telephony.uicc.SimPhonebookRecord;
import java.util.List;
@@ -2657,4 +2658,57 @@
* @param result Message that will be sent back to handler.
*/
default void getSlicingConfig(Message result) {};
+
+ /**
+ * Request the SIM phonebook records of all activated UICC applications
+ *
+ * @param result Callback message containing the count of ADN valid record.
+ */
+ public void getSimPhonebookRecords(Message result);
+
+ /**
+ * Request the SIM phonebook Capacity of all activated UICC applications
+ *
+ */
+ public void getSimPhonebookCapacity(Message result);
+
+ /**
+ * Request to insert/delete/update the SIM phonebook record
+ *
+ * @param phonebookRecordInfo adn record information to be updated
+ * @param result Callback message containing the SIM phonebook record index.
+ */
+ public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecordInfo, Message result);
+
+ /**
+ * Registers the handler when the SIM phonebook is changed.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object .
+ */
+ public void registerForSimPhonebookChanged(Handler h, int what, Object obj);
+
+ /**
+ * Unregister for notifications when SIM phonebook has already init done.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSimPhonebookChanged(Handler h);
+
+ /**
+ * Registers the handler when a group of SIM phonebook records received.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSimPhonebookRecordsReceived(Handler h, int what, Object obj);
+
+ /**
+ * Unregister for notifications when a group of SIM phonebook records received.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSimPhonebookRecordsReceived(Handler h);
}
diff --git a/src/java/com/android/internal/telephony/IIccPhoneBook.aidl b/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
index dc990de..f6e8107 100644
--- a/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
+++ b/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
@@ -16,6 +16,9 @@
package com.android.internal.telephony;
+import android.content.ContentValues;
+
+import com.android.internal.telephony.uicc.AdnCapacity;
import com.android.internal.telephony.uicc.AdnRecord;
/**
@@ -68,49 +71,20 @@
String newTag, String newPhoneNumber,
String pin2);
-
-
/**
* Replace oldAdn with newAdn in ADN-like record in EF
*
* getAdnRecordsInEf must be called at least once before this function,
* otherwise an error will be returned
*
- * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
- * @param oldTag adn tag to be replaced
- * @param oldPhoneNumber adn number to be replaced
- * Set both oldTag and oldPhoneNubmer to "" means to replace an
- * empty record, aka, insert new record
- * @param newTag adn tag to be stored
- * @param newPhoneNumber adn number ot be stored
- * Set both newTag and newPhoneNubmer to "" means to replace the old
- * record with empty one, aka, delete old record
- * @param pin2 required to update EF_FDN, otherwise must be null
* @param subId user preferred subId
- * @return true for success
- */
- boolean updateAdnRecordsInEfBySearchForSubscriber(int subId, int efid,
- String oldTag, String oldPhoneNumber,
- String newTag, String newPhoneNumber,
- String pin2);
- /**
- * Update an ADN-like EF record by record index
- *
- * This is useful for iteration the whole ADN file, such as write the whole
- * phone book or erase/format the whole phonebook
- *
* @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
- * @param newTag adn tag to be stored
- * @param newPhoneNumber adn number to be stored
- * Set both newTag and newPhoneNubmer to "" means to replace the old
- * record with empty one, aka, delete old record
- * @param index is 1-based adn record index to be updated
+ * @param values including ADN,EMAIL,ANR to be updated
* @param pin2 required to update EF_FDN, otherwise must be null
* @return true for success
*/
- boolean updateAdnRecordsInEfByIndex(int efid, String newTag,
- String newPhoneNumber, int index,
- String pin2);
+ boolean updateAdnRecordsInEfBySearchForSubscriber(int subId,
+ int efid, in ContentValues values, String pin2);
/**
* Update an ADN-like EF record by record index
@@ -118,19 +92,15 @@
* This is useful for iteration the whole ADN file, such as write the whole
* phone book or erase/format the whole phonebook
*
+ * @param subId user preferred subId
* @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
- * @param newTag adn tag to be stored
- * @param newPhoneNumber adn number to be stored
- * Set both newTag and newPhoneNubmer to "" means to replace the old
- * record with empty one, aka, delete old record
+ * @param values including ADN,EMAIL,ANR to be updated
* @param index is 1-based adn record index to be updated
* @param pin2 required to update EF_FDN, otherwise must be null
- * @param subId user preferred subId
* @return true for success
*/
- boolean updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid, String newTag,
- String newPhoneNumber, int index,
- String pin2);
+ boolean updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid, in ContentValues values,
+ int index, String pin2);
/**
* Get the max munber of records in efid
@@ -157,4 +127,11 @@
@UnsupportedAppUsage
int[] getAdnRecordsSizeForSubscriber(int subId, int efid);
+ /**
+ * Get the capacity of ADN records
+ *
+ * @param subId user preferred subId
+ * @return AdnCapacity
+ */
+ AdnCapacity getAdnRecordsCapacityForSubscriber(int subId);
}
diff --git a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
index 84a9d26..b78d1ab 100644
--- a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -17,26 +17,32 @@
package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentValues;
import android.content.pm.PackageManager;
import android.os.AsyncResult;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.text.TextUtils;
+import com.android.internal.telephony.uicc.AdnCapacity;
import com.android.internal.telephony.uicc.AdnRecord;
import com.android.internal.telephony.uicc.AdnRecordCache;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.internal.telephony.uicc.IccConstants;
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.SimPhonebookRecordCache;
+import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccProfile;
import com.android.telephony.Rlog;
-import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.List;
/**
- * SimPhoneBookInterfaceManager to provide an inter-process communication to
+ * IccPhoneBookInterfaceManager to provide an inter-process communication to
* access ADN-like SIM records.
*/
public class IccPhoneBookInterfaceManager {
@@ -48,6 +54,7 @@
protected Phone mPhone;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected AdnRecordCache mAdnCache;
+ protected SimPhonebookRecordCache mSimPbRecordCache;
protected static final int EVENT_GET_SIZE_DONE = 1;
protected static final int EVENT_LOAD_DONE = 2;
@@ -118,9 +125,13 @@
if (r != null) {
mAdnCache = r.getAdnCache();
}
+
+ mSimPbRecordCache = new SimPhonebookRecordCache(
+ phone.getContext(), phone.getPhoneId(), phone.mCi);
}
public void dispose() {
+ mSimPbRecordCache.dispose();
}
public void updateIccRecords(IccRecords iccRecords) {
@@ -141,60 +152,81 @@
Rlog.e(LOG_TAG, "[IccPbInterfaceManager] " + msg);
}
+ private AdnRecord generateAdnRecordWithOldTagByContentValues(ContentValues values) {
+ if (values == null) {
+ return null;
+ }
+ final String oldTag = values.getAsString(IccProvider.STR_TAG);
+ final String oldPhoneNumber = values.getAsString(IccProvider.STR_NUMBER);
+ final String oldEmail = values.getAsString(IccProvider.STR_EMAILS);
+ final String oldAnr = values.getAsString(IccProvider.STR_ANRS);;
+ String[] oldEmailArray = TextUtils.isEmpty(oldEmail)
+ ? null : getEmailStringArray(oldEmail);
+ String[] oldAnrArray = TextUtils.isEmpty(oldAnr) ? null : getAnrStringArray(oldAnr);
+ return new AdnRecord(oldTag, oldPhoneNumber, oldEmailArray, oldAnrArray);
+ }
+
+ private AdnRecord generateAdnRecordWithNewTagByContentValues(ContentValues values) {
+ if (values == null) {
+ return null;
+ }
+ final String newTag = values.getAsString(IccProvider.STR_NEW_TAG);
+ final String newPhoneNumber = values.getAsString(IccProvider.STR_NEW_NUMBER);
+ final String newEmail = values.getAsString(IccProvider.STR_NEW_EMAILS);
+ final String newAnr = values.getAsString(IccProvider.STR_NEW_ANRS);
+ String[] newEmailArray = TextUtils.isEmpty(newEmail)
+ ? null : getEmailStringArray(newEmail);
+ String[] newAnrArray = TextUtils.isEmpty(newAnr) ? null : getAnrStringArray(newAnr);
+ return new AdnRecord(newTag, newPhoneNumber, newEmailArray, newAnrArray);
+ }
+
/**
* Replace oldAdn with newAdn in ADN-like record in EF
*
* getAdnRecordsInEf must be called at least once before this function,
- * otherwise an error will be returned. Currently the email field
- * if set in the ADN record is ignored.
+ * otherwise an error will be returned.
* throws SecurityException if no WRITE_CONTACTS permission
*
* @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
- * @param oldTag adn tag to be replaced
- * @param oldPhoneNumber adn number to be replaced
- * Set both oldTag and oldPhoneNubmer to "" means to replace an
- * empty record, aka, insert new record
- * @param newTag adn tag to be stored
- * @param newPhoneNumber adn number ot be stored
- * Set both newTag and newPhoneNubmer to "" means to replace the old
- * record with empty one, aka, delete old record
+ * @param values old adn tag, phone number, email and anr to be replaced
+ * new adn tag, phone number, email and anr to be stored
* @param pin2 required to update EF_FDN, otherwise must be null
* @return true for success
*/
- public boolean
- updateAdnRecordsInEfBySearch (int efid,
- String oldTag, String oldPhoneNumber,
- String newTag, String newPhoneNumber, String pin2) {
-
+ public boolean updateAdnRecordsInEfBySearchForSubscriber(int efid, ContentValues values,
+ String pin2) {
if (mPhone.getContext().checkCallingOrSelfPermission(
- android.Manifest.permission.WRITE_CONTACTS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(
- "Requires android.permission.WRITE_CONTACTS permission");
+ android.Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires android.permission.WRITE_CONTACTS permission");
}
-
- if (DBG) logd("updateAdnRecordsInEfBySearch: efid=0x" +
- Integer.toHexString(efid).toUpperCase() + " ("+ Rlog.pii(LOG_TAG, oldTag) + "," +
- Rlog.pii(LOG_TAG, oldPhoneNumber) + ")" + "==>" + " ("+ Rlog.pii(LOG_TAG, newTag) +
- "," + Rlog.pii(LOG_TAG, newPhoneNumber) + ")"+ " pin2=" + Rlog.pii(LOG_TAG, pin2));
-
efid = updateEfForIccType(efid);
+ if (DBG) {
+ logd("updateAdnRecordsWithContentValuesInEfBySearch: efid=" + efid + ", values = " +
+ values + ", pin2=" + pin2);
+ }
+
checkThread();
Request updateRequest = new Request();
synchronized (updateRequest) {
Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, updateRequest);
- AdnRecord oldAdn = new AdnRecord(oldTag, oldPhoneNumber);
- AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
- if (mAdnCache != null) {
- mAdnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response);
+ AdnRecord oldAdn = generateAdnRecordWithOldTagByContentValues(values);
+ AdnRecord newAdn = generateAdnRecordWithNewTagByContentValues(values);
+ if (usesPbCache(efid)) {
+ mSimPbRecordCache.updateSimPbAdnBySearch(oldAdn, newAdn, response);
waitForResult(updateRequest);
return (boolean) updateRequest.mResult;
} else {
- loge("Failure while trying to update by search due to uninitialised adncache");
- return false;
+ if (mAdnCache != null) {
+ mAdnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response);
+ waitForResult(updateRequest);
+ return (boolean) updateRequest.mResult;
+ } else {
+ loge("Failure while trying to update by search due to uninitialised adncache");
+ return false;
+ }
}
}
}
@@ -210,15 +242,14 @@
* @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
* @param newTag adn tag to be stored
* @param newPhoneNumber adn number to be stored
- * Set both newTag and newPhoneNubmer to "" means to replace the old
+ * Set both newTag and newPhoneNumber to "" means to replace the old
* record with empty one, aka, delete old record
* @param index is 1-based adn record index to be updated
* @param pin2 required to update EF_FDN, otherwise must be null
* @return true for success
*/
public boolean
- updateAdnRecordsInEfByIndex(int efid, String newTag,
- String newPhoneNumber, int index, String pin2) {
+ updateAdnRecordsInEfByIndex(int efid, ContentValues values, int index, String pin2) {
if (mPhone.getContext().checkCallingOrSelfPermission(
android.Manifest.permission.WRITE_CONTACTS)
@@ -226,25 +257,30 @@
throw new SecurityException(
"Requires android.permission.WRITE_CONTACTS permission");
}
-
- if (DBG) logd("updateAdnRecordsInEfByIndex: efid=0x" +
- Integer.toHexString(efid).toUpperCase() + " Index=" + index + " ==> " + "(" +
- Rlog.pii(LOG_TAG, newTag) + "," + Rlog.pii(LOG_TAG, newPhoneNumber) + ")" +
- " pin2=" + Rlog.pii(LOG_TAG, pin2));
-
+ efid = updateEfForIccType(efid);
+ if (DBG) {
+ logd("updateAdnRecordsInEfByIndex: efid=" + efid + ", values = " +
+ values + " index=" + index + ", pin2=" + pin2);
+ }
checkThread();
Request updateRequest = new Request();
synchronized (updateRequest) {
Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, updateRequest);
- AdnRecord newAdn = new AdnRecord(efid, index, newTag, newPhoneNumber);
- if (mAdnCache != null) {
- mAdnCache.updateAdnByIndex(efid, newAdn, index, pin2, response);
+ AdnRecord newAdn = generateAdnRecordWithNewTagByContentValues(values);
+ if (usesPbCache(efid)) {
+ mSimPbRecordCache.updateSimPbAdnByRecordId(index, newAdn, response);
waitForResult(updateRequest);
return (boolean) updateRequest.mResult;
} else {
- loge("Failure while trying to update by index due to uninitialised adncache");
- return false;
+ if (mAdnCache != null) {
+ mAdnCache.updateAdnByIndex(efid, newAdn, index, pin2, response);
+ waitForResult(updateRequest);
+ return (boolean) updateRequest.mResult;
+ } else {
+ loge("Failure while trying to update by index due to uninitialised adncache");
+ return false;
+ }
}
}
}
@@ -301,13 +337,20 @@
Request loadRequest = new Request();
synchronized (loadRequest) {
Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE, loadRequest);
- if (mAdnCache != null) {
- mAdnCache.requestLoadAllAdnLike(efid, mAdnCache.extensionEfForEf(efid), response);
+ if (usesPbCache(efid)) {
+ mSimPbRecordCache.requestLoadAllPbRecords(response);
waitForResult(loadRequest);
return (List<AdnRecord>) loadRequest.mResult;
} else {
- loge("Failure while trying to load from SIM due to uninitialised adncache");
- return null;
+ if (mAdnCache != null) {
+ mAdnCache.requestLoadAllAdnLike(efid,
+ mAdnCache.extensionEfForEf(efid), response);
+ waitForResult(loadRequest);
+ return (List<AdnRecord>) loadRequest.mResult;
+ } else {
+ loge("Failure while trying to load from SIM due to uninitialised adncache");
+ return null;
+ }
}
}
}
@@ -344,5 +387,67 @@
}
return efid;
}
-}
+ private String[] getEmailStringArray(String str) {
+ return str != null ? str.split(",") : null;
+ }
+
+ private String[] getAnrStringArray(String str) {
+ return str != null ? str.split(":") : null;
+ }
+
+ /**
+ * Get the capacity of ADN records
+ *
+ * @return AdnCapacity
+ */
+ public AdnCapacity getAdnRecordsCapacity() {
+ if (DBG) logd("getAdnRecordsCapacity" );
+ if (mPhone.getContext().checkCallingOrSelfPermission(
+ android.Manifest.permission.READ_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Requires android.permission.READ_CONTACTS permission");
+ }
+ int phoneId = mPhone.getPhoneId();
+
+ UiccProfile profile = UiccController.getInstance().getUiccProfileForPhone(phoneId);
+
+ if (profile != null) {
+ IccCardConstants.State cardstate = profile.getState();
+ if (cardstate == IccCardConstants.State.READY
+ || cardstate == IccCardConstants.State.LOADED) {
+ checkThread();
+ AdnCapacity capacity = mSimPbRecordCache.isEnabled()
+ ? mSimPbRecordCache.getAdnCapacity() : null;
+ if (capacity == null) {
+ loge("Adn capacity is null");
+ return null;
+ }
+
+ if (DBG) logd("getAdnRecordsCapacity on slot " + phoneId
+ + ": max adn=" + capacity.getMaxAdnCount()
+ + ", used adn=" + capacity.getUsedAdnCount()
+ + ", max email=" + capacity.getMaxEmailCount()
+ + ", used email=" + capacity.getUsedEmailCount()
+ + ", max anr=" + capacity.getMaxAnrCount()
+ + ", used anr=" + capacity.getUsedAnrCount()
+ + ", max name length="+ capacity.getMaxNameLength()
+ + ", max number length =" + capacity.getMaxNumberLength()
+ + ", max email length =" + capacity.getMaxEmailLength()
+ + ", max anr length =" + capacity.getMaxAnrLength());
+ return capacity;
+ } else {
+ logd("No UICC when getAdnRecordsCapacity.");
+ }
+ } else {
+ logd("sim state is not ready when getAdnRecordsCapacity.");
+ }
+ return null;
+ }
+
+ private boolean usesPbCache(int efid) {
+ return mSimPbRecordCache.isEnabled() &&
+ (efid == IccConstants.EF_PBR || efid == IccConstants.EF_ADN);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IccProvider.java b/src/java/com/android/internal/telephony/IccProvider.java
index b2ac07f..7a128c0 100644
--- a/src/java/com/android/internal/telephony/IccProvider.java
+++ b/src/java/com/android/internal/telephony/IccProvider.java
@@ -31,6 +31,7 @@
import android.telephony.TelephonyFrameworkInitializer;
import android.text.TextUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.uicc.AdnRecord;
import com.android.internal.telephony.uicc.IccConstants;
import com.android.telephony.Rlog;
@@ -51,6 +52,7 @@
"name",
"number",
"emails",
+ "anrs",
"_id"
};
@@ -62,10 +64,24 @@
protected static final int SDN_SUB = 6;
protected static final int ADN_ALL = 7;
- protected static final String STR_TAG = "tag";
- protected static final String STR_NUMBER = "number";
- protected static final String STR_EMAILS = "emails";
- protected static final String STR_PIN2 = "pin2";
+ @VisibleForTesting
+ public static final String STR_TAG = "tag";
+ @VisibleForTesting
+ public static final String STR_NUMBER = "number";
+ @VisibleForTesting
+ public static final String STR_EMAILS = "emails";
+ @VisibleForTesting
+ public static final String STR_ANRS = "anrs";
+ @VisibleForTesting
+ public static final String STR_NEW_TAG = "newTag";
+ @VisibleForTesting
+ public static final String STR_NEW_NUMBER = "newNumber";
+ @VisibleForTesting
+ public static final String STR_NEW_EMAILS = "newEmails";
+ @VisibleForTesting
+ public static final String STR_NEW_ANRS = "newAnrs";
+ @VisibleForTesting
+ public static final String STR_PIN2 = "pin2";
private static final UriMatcher URL_MATCHER =
new UriMatcher(UriMatcher.NO_MATCH);
@@ -203,10 +219,19 @@
"Cannot insert into URL: " + url);
}
- String tag = initialValues.getAsString("tag");
- String number = initialValues.getAsString("number");
- // TODO(): Read email instead of sending null.
- boolean success = addIccRecordToEf(efType, tag, number, null, pin2, subId);
+ // We're not using the incoming initialValues
+ // so we can check/gate the arguments.
+ String tag = initialValues.getAsString(STR_TAG);
+ String number = initialValues.getAsString(STR_NUMBER);
+ String emails = initialValues.getAsString(STR_EMAILS);
+ String anrs = initialValues.getAsString(STR_ANRS);
+
+ ContentValues values = new ContentValues();
+ values.put(STR_NEW_TAG, tag);
+ values.put(STR_NEW_NUMBER, number);
+ values.put(STR_NEW_EMAILS, emails);
+ values.put(STR_NEW_ANRS, anrs);
+ boolean success = updateIccRecordInEf(efType, values, pin2, subId);
if (!success) {
return null;
@@ -299,7 +324,8 @@
// parse where clause
String tag = null;
String number = null;
- String[] emails = null;
+ String emails = null;
+ String anrs = null;
String pin2 = null;
String[] tokens = where.split(" AND ");
@@ -323,18 +349,24 @@
} else if (STR_NUMBER.equals(key)) {
number = normalizeValue(val);
} else if (STR_EMAILS.equals(key)) {
- //TODO(): Email is null.
- emails = null;
+ emails = normalizeValue(val);
+ } else if (STR_ANRS.equals(key)) {
+ anrs = normalizeValue(val);
} else if (STR_PIN2.equals(key)) {
pin2 = normalizeValue(val);
}
}
- if (efType == FDN && TextUtils.isEmpty(pin2)) {
+ ContentValues values = new ContentValues();
+ values.put(STR_TAG, tag);
+ values.put(STR_NUMBER, number);
+ values.put(STR_EMAILS, emails);
+ values.put(STR_ANRS, anrs);
+ if ((efType == FDN) && TextUtils.isEmpty(pin2)) {
return 0;
}
-
- boolean success = deleteIccRecordFromEf(efType, tag, number, emails, pin2, subId);
+ if (DBG) log("delete mvalues= " + values);
+ boolean success = updateIccRecordInEf(efType, values, pin2, subId);
if (!success) {
return 0;
}
@@ -380,15 +412,7 @@
"Cannot insert into URL: " + url);
}
- String tag = values.getAsString("tag");
- String number = values.getAsString("number");
- String[] emails = null;
- String newTag = values.getAsString("newTag");
- String newNumber = values.getAsString("newNumber");
- String[] newEmails = null;
- // TODO(): Update for email.
- boolean success = updateIccRecordInEf(efType, tag, number,
- newTag, newNumber, pin2, subId);
+ boolean success = updateIccRecordInEf(efType, values, pin2, subId);
if (!success) {
return 0;
@@ -435,19 +459,10 @@
}
private boolean
- addIccRecordToEf(int efType, String name, String number, String[] emails,
- String pin2, int subId) {
- if (DBG) log("addIccRecordToEf: efType=0x" + Integer.toHexString(efType).toUpperCase() +
- ", name=" + Rlog.pii(TAG, name) + ", number=" + Rlog.pii(TAG, number) +
- ", emails=" + Rlog.pii(TAG, emails) + ", subscription=" + subId);
-
+ updateIccRecordInEf(int efType, ContentValues values, String pin2, int subId) {
boolean success = false;
-
- // TODO: do we need to call getAdnRecordsInEf() before calling
- // updateAdnRecordsInEfBySearch()? In any case, we will leave
- // the UI level logic to fill that prereq if necessary. But
- // hopefully, we can remove this requirement.
-
+ if (DBG) log("updateIccRecordInEf: efType=" + efType +
+ ", values: [ "+ values + " ], subId:" + subId);
try {
IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
TelephonyFrameworkInitializer
@@ -455,37 +470,9 @@
.getIccPhoneBookServiceRegisterer()
.get());
if (iccIpb != null) {
- success = iccIpb.updateAdnRecordsInEfBySearchForSubscriber(subId, efType,
- "", "", name, number, pin2);
- }
- } catch (RemoteException ex) {
- // ignore it
- } catch (SecurityException ex) {
- if (DBG) log(ex.toString());
- }
- if (DBG) log("addIccRecordToEf: " + success);
- return success;
- }
-
- private boolean
- updateIccRecordInEf(int efType, String oldName, String oldNumber,
- String newName, String newNumber, String pin2, int subId) {
- if (DBG) log("updateIccRecordInEf: efType=0x" + Integer.toHexString(efType).toUpperCase() +
- ", oldname=" + Rlog.pii(TAG, oldName) + ", oldnumber=" + Rlog.pii(TAG, oldNumber) +
- ", newname=" + Rlog.pii(TAG, newName) + ", newnumber=" + Rlog.pii(TAG, newName) +
- ", subscription=" + subId);
-
- boolean success = false;
-
- try {
- IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getIccPhoneBookServiceRegisterer()
- .get());
- if (iccIpb != null) {
- success = iccIpb.updateAdnRecordsInEfBySearchForSubscriber(subId, efType, oldName,
- oldNumber, newName, newNumber, pin2);
+ success = iccIpb
+ .updateAdnRecordsInEfBySearchForSubscriber(
+ subId, efType, values, pin2);
}
} catch (RemoteException ex) {
// ignore it
@@ -496,35 +483,6 @@
return success;
}
-
- private boolean deleteIccRecordFromEf(int efType, String name, String number, String[] emails,
- String pin2, int subId) {
- if (DBG) log("deleteIccRecordFromEf: efType=0x" +
- Integer.toHexString(efType).toUpperCase() + ", name=" + Rlog.pii(TAG, name) +
- ", number=" + Rlog.pii(TAG, number) + ", emails=" + Rlog.pii(TAG, emails) +
- ", pin2=" + Rlog.pii(TAG, pin2) + ", subscription=" + subId);
-
- boolean success = false;
-
- try {
- IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getIccPhoneBookServiceRegisterer()
- .get());
- if (iccIpb != null) {
- success = iccIpb.updateAdnRecordsInEfBySearchForSubscriber(subId, efType,
- name, number, "", "", pin2);
- }
- } catch (RemoteException ex) {
- // ignore it
- } catch (SecurityException ex) {
- if (DBG) log(ex.toString());
- }
- if (DBG) log("deleteIccRecordFromEf: " + success);
- return success;
- }
-
/**
* Loads an AdnRecord into a MatrixCursor. Must be called with mLock held.
*
@@ -534,7 +492,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void loadRecord(AdnRecord record, MatrixCursor cursor, int id) {
if (!record.isEmpty()) {
- Object[] contact = new Object[4];
+ Object[] contact = new Object[5];
String alphaTag = record.getAlphaTag();
String number = record.getNumber();
@@ -552,7 +510,19 @@
}
contact[2] = emailString.toString();
}
- contact[3] = id;
+
+ String[] anrs = record.getAdditionalNumbers();
+ if (anrs != null) {
+ StringBuilder anrString = new StringBuilder();
+ for (String anr : anrs) {
+ if (DBG) log("Adding anr:" + anr);
+ anrString.append(anr);
+ anrString.append(":");
+ }
+ contact[3] = anrString.toString();
+ }
+
+ contact[4] = id;
cursor.addRow(contact);
}
}
diff --git a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
index 54f6aa1..3535678 100644
--- a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
+++ b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
@@ -202,7 +202,6 @@
new NetworkRegStateCallback());
} catch (RemoteException exception) {
// Remote exception means that the binder already died.
- mDeathRecipient.binderDied();
logd("RemoteException " + exception);
}
}
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index cdea374..04c1c46 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -129,6 +129,7 @@
import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.SimPhonebookRecord;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
@@ -6004,6 +6005,100 @@
}
}
+ @Override
+ public void getSimPhonebookRecords(Message result) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ android.hardware.radio.V1_6.IRadio.castFrom(radioProxy);
+ try {
+ radioProxy16.getSimPhonebookRecords(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "getPhonebookRecords", e);
+ }
+ } else {
+ riljLog("Unsupported API in lower than version 1.6 radio HAL" );
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void getSimPhonebookCapacity(Message result) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ android.hardware.radio.V1_6.IRadio.castFrom(radioProxy);
+ try {
+ radioProxy16.getSimPhonebookCapacity(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "getPhonebookRecords", e);
+ }
+ } else {
+ riljLog("Unsupported API in lower than version 1.6 radio HAL" );
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " with " + phonebookRecord.toString());
+ }
+
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ android.hardware.radio.V1_6.IRadio.castFrom(radioProxy);
+
+ android.hardware.radio.V1_6.PhonebookRecordInfo pbRecordInfo =
+ phonebookRecord.toPhonebookRecordInfo();
+ try {
+ radioProxy16.updateSimPhonebookRecords(rr.mSerial, pbRecordInfo);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "updatePhonebookRecord", e);
+ }
+ } else {
+ riljLog("Unsupported API in lower than version 1.6 radio HAL" );
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+ }
+
//***** Private Methods
/** Helper that gets V1.6 of the radio interface OR sends back REQUEST_NOT_SUPPORTED */
@Nullable private android.hardware.radio.V1_6.IRadio getRadioV16(Message msg) {
@@ -6996,6 +7091,12 @@
return "GET_ALLOWED_NETWORK_TYPES_BITMAP";
case RIL_REQUEST_GET_SLICING_CONFIG:
return "GET_SLICING_CONFIG";
+ case RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS:
+ return "GET_SIM_PHONEBOOK_RECORDS";
+ case RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD:
+ return "UPDATE_SIM_PHONEBOOK_RECORD";
+ case RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY:
+ return "GET_SIM_PHONEBOOK_CAPACITY";
default: return "<unknown request>";
}
}
@@ -7119,6 +7220,10 @@
return "UNSOL_REGISTRATION_FAILED";
case RIL_UNSOL_BARRING_INFO_CHANGED:
return "UNSOL_BARRING_INFO_CHANGED";
+ case RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED:
+ return "UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED";
+ case RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED:
+ return "UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED";
default:
return "<unknown response>";
}
diff --git a/src/java/com/android/internal/telephony/RadioConfigResponse.java b/src/java/com/android/internal/telephony/RadioConfigResponse.java
index 97cdc77..8a60fe5 100644
--- a/src/java/com/android/internal/telephony/RadioConfigResponse.java
+++ b/src/java/com/android/internal/telephony/RadioConfigResponse.java
@@ -16,13 +16,14 @@
package com.android.internal.telephony;
-import static android.telephony.TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED;
import static android.telephony.TelephonyManager
.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE;
import static android.telephony.TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED;
import static android.telephony.TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE;
+import static android.telephony.TelephonyManager.CAPABILITY_SIM_PHONEBOOK_IN_MODEM;
import static android.telephony.TelephonyManager.CAPABILITY_SLICING_CONFIG_SUPPORTED;
import static android.telephony.TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING;
+import static android.telephony.TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK;
import static android.telephony.TelephonyManager.RadioInterfaceCapability;
import android.hardware.radio.V1_0.RadioError;
@@ -299,8 +300,8 @@
Rlog.d(TAG, "Radio Hal Version = " + radioHalVersion.toString());
if (radioHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_6)) {
- caps.add(CAPABILITY_ALLOWED_NETWORK_TYPES_USED);
- Rlog.d(TAG, "CAPABILITY_ALLOWED_NETWORK_TYPES_USED");
+ caps.add(CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK);
+ Rlog.d(TAG, "CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK");
if (!modemReducedFeatureSet1) {
caps.add(CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE);
@@ -313,6 +314,8 @@
Rlog.d(TAG, "CAPABILITY_SLICING_CONFIG_SUPPORTED");
caps.add(CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
Rlog.d(TAG, "CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED");
+ caps.add(CAPABILITY_SIM_PHONEBOOK_IN_MODEM);
+ Rlog.d(TAG, "CAPABILITY_SIM_PHONEBOOK_IN_MODEM");
}
}
return caps;
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index 00d0830..d1ed81b 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -68,6 +68,8 @@
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UNTHROTTLE_APN;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_VOICE_RADIO_TECH_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOl_CDMA_PRL_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED;
import android.hardware.radio.V1_0.CdmaCallWaiting;
import android.hardware.radio.V1_0.CdmaInformationRecord;
@@ -114,8 +116,10 @@
import com.android.internal.telephony.dataconnection.KeepaliveStatus;
import com.android.internal.telephony.gsm.SsData;
import com.android.internal.telephony.gsm.SuppServiceNotification;
+import com.android.internal.telephony.uicc.ReceivedPhonebookRecords;
import com.android.internal.telephony.uicc.IccRefreshResponse;
import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.SimPhonebookRecord;
import java.util.ArrayList;
import java.util.List;
@@ -1078,18 +1082,39 @@
* @param indicationType RadioIndicationType
*/
public void simPhonebookChanged(int indicationType) {
+ mRil.processIndication(indicationType);
+ if (RIL.RILJ_LOGD) {
+ mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED);
+ }
+
+ mRil.mSimPhonebookChangedRegistrants.notifyRegistrants();
}
/**
- * Indicates the content of all the used records in the SIM phonebook.
- *
+ * Indicates the content of all the used records in the SIM phonebook..
* @param indicationType RadioIndicationType
* @param records Content of the SIM phonebook records
*/
public void simPhonebookRecordsReceived(int indicationType, byte status,
ArrayList<PhonebookRecordInfo> records) {
+ mRil.processIndication(indicationType);
+ List<SimPhonebookRecord> simPhonebookRecords = new ArrayList<SimPhonebookRecord>();
+
+ for (PhonebookRecordInfo record : records) {
+ simPhonebookRecords.add(new SimPhonebookRecord(record));
+ }
+
+ if (RIL.RILJ_LOGD) {
+ mRil.unsljLogRet(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED,
+ "status = " + status +
+ " received " + records.size() + " records");
+ }
+
+ mRil.mSimPhonebookRecordsReceivedRegistrants.notifyRegistrants(
+ new AsyncResult(null,
+ new ReceivedPhonebookRecords(status, simPhonebookRecords), null));
}
/**
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index b1332a1..9e08b9c 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -63,6 +63,7 @@
import com.android.internal.telephony.dataconnection.KeepaliveStatus;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.telephony.uicc.AdnCapacity;
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccIoResult;
@@ -2014,7 +2015,8 @@
public void getSimPhonebookCapacityResponse(
android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
android.hardware.radio.V1_6.PhonebookCapacity pbCapacity) {
- responseVoid_1_6(responseInfo);
+ AdnCapacity capacity = new AdnCapacity(pbCapacity);
+ responseAdnCapacity(responseInfo, capacity);
}
/**
@@ -2024,7 +2026,19 @@
public void updateSimPhonebookRecordsResponse(
android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
int updatedRecordIndex) {
- responseVoid_1_6(responseInfo);
+ responseInts_1_6(responseInfo, updatedRecordIndex);
+ }
+
+ private void responseAdnCapacity(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ AdnCapacity capacity) {
+ RILRequest rr = mRil.processResponse_1_6(responseInfo);
+ if (rr != null) {
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, capacity);
+ }
+ mRil.processResponseDone_1_6(rr, responseInfo, capacity);
+ }
}
private void responseIccCardStatus(RadioResponseInfo responseInfo, CardStatus cardStatus) {
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 48e3871..6633f80 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -2516,9 +2516,9 @@
if (carrierPackages != null && carrierPackages.size() == 1) {
return carrierPackages.get(0);
}
- // If there is no carrier package which implements CarrierMessagingService, then lookup if
- // for a carrierImsPackage that implements CarrierMessagingService.
- return CarrierSmsUtils.getCarrierImsPackageForIntent(mContext, mPhone,
+ // If there is no carrier package which implements CarrierMessagingService, then lookup
+ // an ImsService implementing RCS that also implements CarrierMessagingService.
+ return CarrierSmsUtils.getImsRcsPackageForIntent(mContext, mPhone,
new Intent(CarrierMessagingService.SERVICE_INTERFACE));
}
diff --git a/src/java/com/android/internal/telephony/SmsPermissions.java b/src/java/com/android/internal/telephony/SmsPermissions.java
index 3837881..44751ac 100644
--- a/src/java/com/android/internal/telephony/SmsPermissions.java
+++ b/src/java/com/android/internal/telephony/SmsPermissions.java
@@ -78,14 +78,14 @@
/**
* Enforces that the caller is one of the following apps:
* <ul>
- * <li> IMS App
+ * <li> IMS App determined by telephony to implement RCS features
* <li> Carrier App
* </ul>
*/
public void enforceCallerIsImsAppOrCarrierApp(String message) {
- String carrierImsPackage = CarrierSmsUtils.getCarrierImsPackageForIntent(mContext,
+ String imsRcsPackage = CarrierSmsUtils.getImsRcsPackageForIntent(mContext,
mPhone, new Intent(CarrierMessagingService.SERVICE_INTERFACE));
- if (carrierImsPackage != null && packageNameMatchesCallingUid(carrierImsPackage)) {
+ if (imsRcsPackage != null && packageNameMatchesCallingUid(imsRcsPackage)) {
return;
}
TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
diff --git a/src/java/com/android/internal/telephony/UiccPhoneBookController.java b/src/java/com/android/internal/telephony/UiccPhoneBookController.java
index 34be94c..5b55c35 100644
--- a/src/java/com/android/internal/telephony/UiccPhoneBookController.java
+++ b/src/java/com/android/internal/telephony/UiccPhoneBookController.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2008 The Android Open Source Project
- * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, 2021 The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +22,9 @@
import android.os.Build;
import android.os.TelephonyServiceManager.ServiceRegisterer;
import android.telephony.TelephonyFrameworkInitializer;
+import android.content.ContentValues;
+import com.android.internal.telephony.uicc.AdnCapacity;
import com.android.internal.telephony.uicc.AdnRecord;
import com.android.telephony.Rlog;
@@ -46,44 +48,24 @@
public boolean
updateAdnRecordsInEfBySearch (int efid, String oldTag, String oldPhoneNumber,
String newTag, String newPhoneNumber, String pin2) throws android.os.RemoteException {
- return updateAdnRecordsInEfBySearchForSubscriber(getDefaultSubscription(), efid, oldTag,
- oldPhoneNumber, newTag, newPhoneNumber, pin2);
+ ContentValues values = new ContentValues();
+ values.put(IccProvider.STR_TAG, oldTag);
+ values.put(IccProvider.STR_NUMBER, oldPhoneNumber);
+ values.put(IccProvider.STR_NEW_TAG, newTag);
+ values.put(IccProvider.STR_NEW_NUMBER, newPhoneNumber);
+ return updateAdnRecordsInEfBySearchForSubscriber(getDefaultSubscription(),
+ efid, values, pin2);
}
@Override
public boolean
- updateAdnRecordsInEfBySearchForSubscriber(int subId, int efid, String oldTag,
- String oldPhoneNumber, String newTag, String newPhoneNumber,
- String pin2) throws android.os.RemoteException {
+ updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid, ContentValues values,
+ int index, String pin2) throws android.os.RemoteException {
IccPhoneBookInterfaceManager iccPbkIntMgr =
getIccPhoneBookInterfaceManager(subId);
if (iccPbkIntMgr != null) {
- return iccPbkIntMgr.updateAdnRecordsInEfBySearch(efid, oldTag,
- oldPhoneNumber, newTag, newPhoneNumber, pin2);
- } else {
- Rlog.e(TAG,"updateAdnRecordsInEfBySearch iccPbkIntMgr is" +
- " null for Subscription:"+subId);
- return false;
- }
- }
-
- @Override
- public boolean
- updateAdnRecordsInEfByIndex(int efid, String newTag,
- String newPhoneNumber, int index, String pin2) throws android.os.RemoteException {
- return updateAdnRecordsInEfByIndexForSubscriber(getDefaultSubscription(), efid, newTag,
- newPhoneNumber, index, pin2);
- }
-
- @Override
- public boolean
- updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid, String newTag,
- String newPhoneNumber, int index, String pin2) throws android.os.RemoteException {
- IccPhoneBookInterfaceManager iccPbkIntMgr =
- getIccPhoneBookInterfaceManager(subId);
- if (iccPbkIntMgr != null) {
- return iccPbkIntMgr.updateAdnRecordsInEfByIndex(efid, newTag,
- newPhoneNumber, index, pin2);
+ return iccPbkIntMgr.updateAdnRecordsInEfByIndex(efid, values,
+ index, pin2);
} else {
Rlog.e(TAG,"updateAdnRecordsInEfByIndex iccPbkIntMgr is" +
" null for Subscription:"+subId);
@@ -129,6 +111,34 @@
}
}
+ @Override
+ public AdnCapacity getAdnRecordsCapacityForSubscriber(int subId)
+ throws android.os.RemoteException {
+ IccPhoneBookInterfaceManager iccPbkIntMgr = getIccPhoneBookInterfaceManager(subId);
+ if (iccPbkIntMgr != null) {
+ return iccPbkIntMgr.getAdnRecordsCapacity();
+ } else {
+ Rlog.e(TAG, "getAdnRecordsCapacity iccPbkIntMgr is null for Subscription:" + subId);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean
+ updateAdnRecordsInEfBySearchForSubscriber(int subId, int efid,
+ ContentValues values, String pin2)
+ throws android.os.RemoteException {
+ IccPhoneBookInterfaceManager iccPbkIntMgr = getIccPhoneBookInterfaceManager(subId);
+ if (iccPbkIntMgr != null) {
+ return iccPbkIntMgr.updateAdnRecordsInEfBySearchForSubscriber(
+ efid, values, pin2);
+ } else {
+ Rlog.e(TAG,"updateAdnRecordsInEfBySearchForSubscriber " +
+ "iccPbkIntMgr is null for Subscription:"+subId);
+ return false;
+ }
+ }
+
/**
* get phone book interface manager object based on subscription.
**/
diff --git a/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java b/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
index 3b5e0e7..53b7515 100644
--- a/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
@@ -194,7 +194,6 @@
registerDataThrottlersFirstTime();
} catch (RemoteException e) {
- mDeathRecipient.binderDied();
loge("Remote exception. " + e);
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
index 91a6d96..3ba577a 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
@@ -222,6 +222,7 @@
mDeathRecipient = new DataServiceManagerDeathRecipient();
mBound = true;
mLastBoundPackageName = getDataServicePackageName();
+ removeMessages(EVENT_WATCHDOG_TIMEOUT);
try {
service.linkToDeath(mDeathRecipient, 0);
@@ -231,11 +232,9 @@
mIDataService.registerForUnthrottleApn(mPhone.getPhoneId(),
new CellularDataServiceCallback("unthrottleApn"));
} catch (RemoteException e) {
- mDeathRecipient.binderDied();
loge("Remote exception. " + e);
return;
}
- removeMessages(EVENT_WATCHDOG_TIMEOUT);
mServiceBindingChangedRegistrants.notifyResult(true);
}
@Override
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index b717582..399baa5 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -69,6 +69,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -128,6 +129,27 @@
// Delay between dynamic ImsService queries.
private static final int DELAY_DYNAMIC_QUERY_MS = 5000;
+ private static ImsResolver sInstance;
+
+ /**
+ * Create the ImsResolver Service singleton instance.
+ */
+ public static void make(Context context, String defaultMmTelPackageName,
+ String defaultRcsPackageName, int numSlots, ImsFeatureBinderRepository repo) {
+ if (sInstance == null) {
+ sInstance = new ImsResolver(context, defaultMmTelPackageName, defaultRcsPackageName,
+ numSlots, repo);
+ }
+ }
+
+ /**
+ * @return The ImsResolver Service instance. May be {@code null} if no ImsResolver was created
+ * due to IMS not being supported.
+ */
+ public static @Nullable ImsResolver getInstance() {
+ return sInstance;
+ }
+
private static class OverrideConfig {
public final int slotId;
public final boolean isCarrierService;
@@ -830,7 +852,18 @@
return false;
}
// Config exists, but the carrier ImsService also needs to support this feature
- ImsServiceInfo info = getImsServiceInfoFromCache(carrierPackage);
+ return doesCachedImsServiceExist(carrierPackage, slotId, featureType);
+ }
+
+ /**
+ * Check the cached ImsServices that exist on this device to determine if there is a ImsService
+ * with the same package name that matches the provided configuration.
+ */
+ // not synchronized, access in handler ONLY.
+ private boolean doesCachedImsServiceExist(String packageName, int slotId,
+ @ImsFeature.FeatureType int featureType) {
+ // Config exists, but the carrier ImsService also needs to support this feature
+ ImsServiceInfo info = getImsServiceInfoFromCache(packageName);
return info != null && info.getSupportedFeatures().stream().anyMatch(
feature -> feature.slotId == slotId && feature.featureType == featureType);
}
@@ -858,6 +891,94 @@
return null;
}
}
+ /**
+ * Resolves the PackageName of the ImsService that is configured to be bound for the slotId and
+ * FeatureType specified and returns it.
+ * <p>
+ * If there is a PackageName that is configured, but there is no application on the device that
+ * fulfills that configuration, this method will also return {@code null} as the ImsService will
+ * not be bound.
+ *
+ * @param slotId The slot ID that the request is for.
+ * @param featureType The ImsService feature type that the request is for.
+ * @return The package name of the ImsService that will be bound from telephony for the provided
+ * slot id and featureType.
+ */
+ public String getConfiguredImsServicePackageName(int slotId,
+ @ImsFeature.FeatureType int featureType) {
+ if (slotId < 0 || slotId >= mNumSlots || featureType <= ImsFeature.FEATURE_INVALID
+ || featureType >= ImsFeature.FEATURE_MAX) {
+ Log.w(TAG, "getResolvedImsServicePackageName received invalid parameters - slot: "
+ + slotId + ", feature: " + featureType);
+ return null;
+ }
+ CompletableFuture<String> packageNameFuture = new CompletableFuture<>();
+ if (mHandler.getLooper().isCurrentThread()) {
+ // If we are on the same thread as the Handler's looper, run the internal method
+ // directly.
+ packageNameFuture.complete(getConfiguredImsServicePackageNameInternal(slotId,
+ featureType));
+ } else {
+ mEventLog.log("getResolvedImsServicePackageName - [" + slotId + ", "
+ + ImsFeature.FEATURE_LOG_MAP.get(featureType) + "], starting query...");
+ Log.d(TAG, "getResolvedImsServicePackageName: [" + slotId + ", "
+ + ImsFeature.FEATURE_LOG_MAP.get(featureType) + "], starting query...");
+ mHandler.post(() -> {
+ try {
+ packageNameFuture.complete(getConfiguredImsServicePackageNameInternal(slotId,
+ featureType));
+ } catch (Exception e) {
+ // Catch all Exceptions to ensure we do not block indefinitely in the case of an
+ // unexpected error.
+ packageNameFuture.completeExceptionally(e);
+ }
+ });
+ }
+ try {
+ String packageName = packageNameFuture.get();
+ mEventLog.log("getResolvedImsServicePackageName - [" + slotId + ", "
+ + ImsFeature.FEATURE_LOG_MAP.get(featureType)
+ + "], async query complete with package name: " + packageName);
+ Log.d(TAG, "getResolvedImsServicePackageName: [" + slotId + ", "
+ + ImsFeature.FEATURE_LOG_MAP.get(featureType)
+ + "], async query complete with package name: " + packageName);
+ return packageName;
+ } catch (Exception e) {
+ mEventLog.log("getResolvedImsServicePackageName - [" + slotId + ", "
+ + ImsFeature.FEATURE_LOG_MAP.get(featureType) + "] -> Exception: " + e);
+ Log.w(TAG, "getResolvedImsServicePackageName: [" + slotId + ", "
+ + ImsFeature.FEATURE_LOG_MAP.get(featureType) + "] returned Exception: " + e);
+ return null;
+ }
+ }
+
+ /**
+ * @return the package name for the configured carrier ImsService if it exists on the device and
+ * supports the supplied slotId and featureType. If no such configuration exists, fall back to
+ * the device ImsService. If neither exist, then return {@code null};
+ */
+ // Not synchronized, access on Handler ONLY!
+ private String getConfiguredImsServicePackageNameInternal(int slotId,
+ @ImsFeature.FeatureType int featureType) {
+ // If a carrier ImsService is configured to be used for the provided slotId and
+ // featureType, then return that one.
+ String carrierPackage = getCarrierConfiguredPackageName(slotId, featureType);
+ if (!TextUtils.isEmpty(carrierPackage)
+ && doesCachedImsServiceExist(carrierPackage, slotId, featureType)) {
+ return carrierPackage;
+ }
+ // If there is no carrier ImsService configured for that configuration, then
+ // return the device's default ImsService for the provided slotId and
+ // featureType.
+ String devicePackage = getDeviceConfiguration(featureType);
+ if (!TextUtils.isEmpty(devicePackage)
+ && doesCachedImsServiceExist(devicePackage, slotId, featureType)) {
+ return devicePackage;
+ }
+ // There is no ImsService configuration that exists for the slotId and
+ // featureType.
+ return null;
+ }
private void putImsController(int slotId, int feature, ImsServiceController controller) {
if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID
diff --git a/src/java/com/android/internal/telephony/uicc/AdnCapacity.aidl b/src/java/com/android/internal/telephony/uicc/AdnCapacity.aidl
new file mode 100644
index 0000000..61ae736
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/AdnCapacity.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 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;
+
+parcelable AdnCapacity;
diff --git a/src/java/com/android/internal/telephony/uicc/AdnCapacity.java b/src/java/com/android/internal/telephony/uicc/AdnCapacity.java
new file mode 100644
index 0000000..300759a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/AdnCapacity.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 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.hardware.radio.V1_6.PhonebookCapacity;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Used to present ADN capacity
+ *
+ * {@hide}
+ */
+public class AdnCapacity implements Parcelable {
+
+ private int mMaxAdnCount;
+ private int mUsedAdnCount;
+ private int mMaxEmailCount;
+ private int mUsedEmailCount;
+ private int mMaxAnrCount;
+ private int mUsedAnrCount;
+ private int mMaxNameLength;
+ private int mMaxNumberLength;
+ private int mMaxEmailLength;
+ private int mMaxAnrLength;
+
+ private int mHashCode = 0;
+
+ public AdnCapacity(int maxAdnCount, int usedAdnCount, int maxEmailCount,
+ int usedEmailCount, int maxAnrCount, int usedAnrCount, int maxNameLength,
+ int maxNumberLength, int maxEmailLength, int maxAnrLength) {
+ mMaxAdnCount = maxAdnCount;
+ mUsedAdnCount = usedAdnCount;
+ mMaxEmailCount = maxEmailCount;
+ mUsedEmailCount = usedEmailCount;
+ mMaxAnrCount = maxAnrCount;
+ mUsedAnrCount = usedAnrCount;
+ mMaxNameLength = maxNameLength;
+ mMaxNumberLength = maxNumberLength;
+ mMaxEmailLength = maxEmailLength;
+ mMaxAnrLength = maxAnrLength;
+ }
+
+ public AdnCapacity(PhonebookCapacity pbCap) {
+ if (pbCap != null) {
+ mMaxAdnCount = pbCap.maxAdnRecords;
+ mUsedAdnCount = pbCap.usedAdnRecords;
+ mMaxEmailCount = pbCap.maxEmailRecords;
+ mUsedEmailCount = pbCap.usedEmailRecords;
+ mMaxAnrCount = pbCap.maxAdditionalNumberRecords;
+ mUsedAnrCount = pbCap.usedAdditionalNumberRecords;
+ mMaxNameLength = pbCap.maxNameLen;
+ mMaxNumberLength = pbCap.maxNumberLen;
+ mMaxEmailLength = pbCap.maxEmailLen;
+ mMaxAnrLength = pbCap.maxAdditionalNumberLen;
+ }
+ }
+
+ public int getMaxAdnCount() {
+ return mMaxAdnCount;
+ }
+
+ public int getUsedAdnCount() {
+ return mUsedAdnCount;
+ }
+
+ public int getMaxEmailCount() {
+ return mMaxEmailCount;
+ }
+
+ public int getUsedEmailCount() {
+ return mUsedEmailCount;
+ }
+
+ public int getMaxAnrCount() {
+ return mMaxAnrCount;
+ }
+
+ public int getUsedAnrCount() {
+ return mUsedAnrCount;
+ }
+
+ public int getMaxNameLength() {
+ return mMaxNameLength;
+ }
+
+ public int getMaxNumberLength() {
+ return mMaxNumberLength;
+ }
+
+ public int getMaxEmailLength() {
+ return mMaxEmailLength;
+ }
+
+ public int getMaxAnrLength() {
+ return mMaxAnrLength;
+ }
+
+ public boolean isSimFull() {
+ return mMaxAdnCount == mUsedAdnCount;
+ }
+
+ public static final Parcelable.Creator<AdnCapacity> CREATOR
+ = new Parcelable.Creator<AdnCapacity>() {
+ @Override
+ public AdnCapacity createFromParcel(Parcel source) {
+ final int maxAdnCount = source.readInt();
+ final int usedAdnCount = source.readInt();
+ final int maxEmailCount = source.readInt();
+ final int usedEmailCount = source.readInt();
+ final int maxAnrCount = source.readInt();
+ final int usedAnrCount = source.readInt();
+ final int maxNameLength = source.readInt();
+ final int maxNumberLength = source.readInt();
+ final int maxEmailLength = source.readInt();
+ final int maxAnrLength = source.readInt();
+ return new AdnCapacity(maxAdnCount, usedAdnCount, maxEmailCount,
+ usedEmailCount, maxAnrCount, usedAnrCount, maxNameLength,
+ maxNumberLength, maxEmailLength, maxAnrLength);
+ }
+
+ @Override
+ public AdnCapacity[] newArray(int size) {
+ return new AdnCapacity[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mMaxAdnCount);
+ dest.writeInt(mUsedAdnCount);
+ dest.writeInt(mMaxEmailCount);
+ dest.writeInt(mUsedEmailCount);
+ dest.writeInt(mMaxAnrCount);
+ dest.writeInt(mUsedAnrCount);
+ dest.writeInt(mMaxNameLength);
+ dest.writeInt(mMaxNumberLength);
+ dest.writeInt(mMaxEmailLength);
+ dest.writeInt(mMaxAnrLength);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof AdnCapacity) {
+ AdnCapacity capacity = (AdnCapacity)obj;
+ return capacity.getMaxAdnCount() == mMaxAdnCount
+ && capacity.getUsedAdnCount() == mUsedAdnCount
+ && capacity.getMaxEmailCount() == mMaxEmailCount
+ && capacity.getUsedEmailCount() == mUsedEmailCount
+ && capacity.getMaxAnrCount() == mMaxAnrCount
+ && capacity.getUsedAnrCount() == mUsedAnrCount
+ && capacity.getMaxNameLength() == mMaxNameLength
+ && capacity.getMaxNumberLength() == mMaxNumberLength
+ && capacity.getMaxEmailLength() == mMaxEmailLength
+ && capacity.getMaxAnrLength() == mMaxAnrLength;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ if (mHashCode == 0) {
+ mHashCode = mMaxAdnCount;
+ mHashCode = 31 * mHashCode + mUsedAdnCount;
+ mHashCode = 31 * mHashCode + mMaxEmailCount;
+ mHashCode = 31 * mHashCode + mUsedEmailCount;
+ mHashCode = 31 * mHashCode + mMaxAnrCount;
+ mHashCode = 31 * mHashCode + mUsedAnrCount;
+ mHashCode = 31 * mHashCode + mMaxNameLength;
+ mHashCode = 31 * mHashCode + mMaxNumberLength;
+ mHashCode = 31 * mHashCode + mMaxEmailLength;
+ mHashCode = 31 * mHashCode + mMaxAnrLength;
+ }
+ return mHashCode;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/AdnRecord.java b/src/java/com/android/internal/telephony/uicc/AdnRecord.java
index ce0fc79..0d23ce7 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnRecord.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnRecord.java
@@ -24,9 +24,11 @@
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
+import com.android.internal.util.ArrayUtils;
import com.android.telephony.Rlog;
import java.util.Arrays;
+import java.util.List;
/**
*
@@ -46,6 +48,7 @@
String mNumber = null;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String[] mEmails;
+ String[] mAdditionalNumbers = null;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int mExtRecord = 0xff;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -88,14 +91,16 @@
String alphaTag;
String number;
String[] emails;
+ String[] additionalNumbers;
efid = source.readInt();
recordNumber = source.readInt();
alphaTag = source.readString();
number = source.readString();
emails = source.createStringArray();
+ additionalNumbers = source.createStringArray();
- return new AdnRecord(efid, recordNumber, alphaTag, number, emails);
+ return new AdnRecord(efid, recordNumber, alphaTag, number, emails, additionalNumbers);
}
@Override
@@ -170,6 +175,10 @@
this(0, 0, alphaTag, number, emails);
}
+ public AdnRecord(String alphaTag, String number, String[] emails, String[] additionalNumbers) {
+ this(0, 0, alphaTag, number, emails, additionalNumbers);
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public AdnRecord (int efid, int recordNumber, String alphaTag, String number, String[] emails) {
this.mEfid = efid;
@@ -177,6 +186,17 @@
this.mAlphaTag = alphaTag;
this.mNumber = number;
this.mEmails = emails;
+ this.mAdditionalNumbers = null;
+ }
+
+ public AdnRecord(int efid, int recordNumber, String alphaTag, String number, String[] emails,
+ String[] additionalNumbers) {
+ this.mEfid = efid;
+ this.mRecordNumber = recordNumber;
+ this.mAlphaTag = alphaTag;
+ this.mNumber = number;
+ this.mEmails = emails;
+ this.mAdditionalNumbers = additionalNumbers;
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -186,6 +206,7 @@
this.mAlphaTag = alphaTag;
this.mNumber = number;
this.mEmails = null;
+ this.mAdditionalNumbers = null;
}
//***** Instance Methods
@@ -202,6 +223,10 @@
return mRecordNumber;
}
+ public void setRecId(int recordId) {
+ mRecordNumber = recordId;
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getNumber() {
return mNumber;
@@ -221,15 +246,25 @@
this.mEmails = emails;
}
+ public String[] getAdditionalNumbers() {
+ return mAdditionalNumbers;
+ }
+
+ public void setAdditionalNumbers(String[] additionalNumbers) {
+ mAdditionalNumbers = additionalNumbers;
+ }
+
@Override
public String toString() {
return "ADN Record '" + mAlphaTag + "' '" + Rlog.pii(LOG_TAG, mNumber) + " "
- + Rlog.pii(LOG_TAG, mEmails) + "'";
+ + Rlog.pii(LOG_TAG, mEmails) + " "
+ + Rlog.pii(LOG_TAG, mAdditionalNumbers) + "'";
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isEmpty() {
- return TextUtils.isEmpty(mAlphaTag) && TextUtils.isEmpty(mNumber) && mEmails == null;
+ return TextUtils.isEmpty(mAlphaTag) && TextUtils.isEmpty(mNumber)
+ && mEmails == null && mAdditionalNumbers == null;
}
public boolean hasExtendedRecord() {
@@ -250,10 +285,30 @@
return (s1.equals(s2));
}
+ /** Help function for ANR/EMAIL array compare. */
+ private static boolean arrayCompareNullEqualsEmpty(String s1[], String s2[]) {
+ if (s1 == s2) {
+ return true;
+ }
+
+ s1 = ArrayUtils.emptyIfNull(s1, String.class);
+ s2 = ArrayUtils.emptyIfNull(s2, String.class);
+
+ List<String> src = Arrays.asList(s1);
+ List<String> dest = Arrays.asList(s2);
+
+ if (src.size() != dest.size()) {
+ return false;
+ }
+
+ return src.containsAll(dest);
+ }
+
public boolean isEqual(AdnRecord adn) {
return ( stringCompareNullEqualsEmpty(mAlphaTag, adn.mAlphaTag) &&
stringCompareNullEqualsEmpty(mNumber, adn.mNumber) &&
- Arrays.equals(mEmails, adn.mEmails));
+ arrayCompareNullEqualsEmpty(mEmails, adn.mEmails) &&
+ arrayCompareNullEqualsEmpty(mAdditionalNumbers, adn.mAdditionalNumbers));
}
//***** Parcelable Implementation
@@ -269,6 +324,7 @@
dest.writeString(mAlphaTag);
dest.writeString(mNumber);
dest.writeStringArray(mEmails);
+ dest.writeStringArray(mAdditionalNumbers);
}
/**
@@ -292,10 +348,10 @@
adnString[i] = (byte) 0xFF;
}
- if (TextUtils.isEmpty(mNumber)) {
+ if (TextUtils.isEmpty(mNumber) && TextUtils.isEmpty(mAlphaTag)) {
Rlog.w(LOG_TAG, "[buildAdnString] Empty dialing number");
return adnString; // return the empty record (for delete)
- } else if (mNumber.length()
+ } else if (mNumber != null && mNumber.length()
> (ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1) * 2) {
Rlog.w(LOG_TAG,
"[buildAdnString] Max length of dialing number is 20");
@@ -308,14 +364,16 @@
Rlog.w(LOG_TAG, "[buildAdnString] Max length of tag is " + footerOffset);
return null;
} else {
- bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
- mNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
+ if (!TextUtils.isEmpty(mNumber)) {
+ bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
+ mNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
- System.arraycopy(bcdNumber, 0, adnString,
- footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
+ System.arraycopy(bcdNumber, 0, adnString,
+ footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
- adnString[footerOffset + ADN_BCD_NUMBER_LENGTH]
- = (byte) (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]
@@ -400,12 +458,13 @@
mExtRecord = 0xff & record[record.length - 1];
mEmails = null;
-
+ mAdditionalNumbers = null;
} catch (RuntimeException ex) {
Rlog.w(LOG_TAG, "Error parsing AdnRecord", ex);
mNumber = "";
mAlphaTag = "";
mEmails = null;
+ mAdditionalNumbers = null;
}
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/ReceivedPhonebookRecords.java b/src/java/com/android/internal/telephony/uicc/ReceivedPhonebookRecords.java
new file mode 100644
index 0000000..16fb55b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/ReceivedPhonebookRecords.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 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.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Represents the received ADN entries from the SIM.
+ *
+ * {@hide}
+ */
+public class ReceivedPhonebookRecords {
+ @PhonebookReceivedState
+ private int mPhonebookReceivedState;
+ private List<SimPhonebookRecord> mEntries;
+
+ @IntDef(value = {
+ RS_OK,
+ RS_ERROR,
+ RS_ABORT,
+ RS_FINAL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PhonebookReceivedState {}
+
+ public static final int RS_OK = 1;
+ public static final int RS_ERROR = 2;
+ public static final int RS_ABORT = 3;
+ public static final int RS_FINAL = 4;
+
+ public ReceivedPhonebookRecords(@PhonebookReceivedState int state,
+ List<SimPhonebookRecord> entries) {
+ mPhonebookReceivedState = state;
+ mEntries = entries;
+ }
+
+ public boolean isCompleted() {
+ return mPhonebookReceivedState == RS_FINAL;
+ }
+
+ public boolean isRetryNeeded() {
+ return mPhonebookReceivedState == RS_ABORT;
+ }
+
+ public boolean isOk() {
+ return mPhonebookReceivedState == RS_OK;
+ }
+ public List<SimPhonebookRecord> getPhonebookRecords() {
+ return mEntries;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/SimPhonebookRecord.java b/src/java/com/android/internal/telephony/uicc/SimPhonebookRecord.java
new file mode 100644
index 0000000..c6c7d6d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/SimPhonebookRecord.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2021 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.hardware.radio.V1_6.PhonebookRecordInfo;
+import android.text.TextUtils;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.util.ArrayUtils;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents a Phonebook entry from the SIM.
+ *
+ * {@hide}
+ */
+public class SimPhonebookRecord {
+ // Instance variables
+ private int mRecordIndex = 0;
+ private String mAlphaTag;
+ private String mNumber;
+ private String[] mEmails;
+ private String[] mAdditionalNumbers;
+
+ // Instance methods
+ public SimPhonebookRecord (int recordIndex, String alphaTag, String number,
+ String[] emails, String[] adNumbers) {
+ mRecordIndex = recordIndex;
+ mAlphaTag = alphaTag;
+ mNumber = convertRecordFormatToNumber(number);
+ mEmails = emails;
+ if (adNumbers != null) {
+ mAdditionalNumbers = new String[adNumbers.length];
+ for (int i = 0 ; i < adNumbers.length; i++) {
+ mAdditionalNumbers[i] =
+ convertRecordFormatToNumber(adNumbers[i]);
+ }
+ }
+ }
+
+ public SimPhonebookRecord(PhonebookRecordInfo recInfo) {
+ mRecordIndex = recInfo.recordId;
+ mAlphaTag = recInfo.name;
+ mNumber = recInfo.number;
+ mEmails = recInfo.emails == null ? null
+ : recInfo.emails.toArray(new String[recInfo.emails.size()]);
+ mAdditionalNumbers = recInfo.additionalNumbers == null ? null
+ : recInfo.additionalNumbers.toArray(
+ new String[recInfo.additionalNumbers.size()]);
+ }
+
+ public SimPhonebookRecord() {}
+
+ public PhonebookRecordInfo toPhonebookRecordInfo() {
+ PhonebookRecordInfo pbRecordInfo = new PhonebookRecordInfo();
+ pbRecordInfo.recordId = mRecordIndex;
+ pbRecordInfo.name = convertNullToEmptyString(mAlphaTag);
+ pbRecordInfo.number = convertNullToEmptyString(convertNumberToRecordFormat(mNumber));
+ if (mEmails != null) {
+ for (String email : mEmails) {
+ pbRecordInfo.emails.add(email);
+ }
+ }
+ if (mAdditionalNumbers != null) {
+ for (String addNum : mAdditionalNumbers) {
+ pbRecordInfo.additionalNumbers.add(convertNumberToRecordFormat(addNum));
+ }
+ }
+ return pbRecordInfo;
+ }
+ public int getRecordIndex() {
+ return mRecordIndex;
+ }
+
+ public String getAlphaTag() {
+ return mAlphaTag;
+ }
+
+ public String getNumber() {
+ return mNumber;
+ }
+
+ public String[] getEmails() {
+ return mEmails;
+ }
+
+ public String[] getAdditionalNumbers() {
+ return mAdditionalNumbers;
+ }
+
+ /**
+ * convert phone number in the SIM phonebook record format to GSM pause/wild/wait character
+ */
+ private static String convertRecordFormatToNumber(String input) {
+ return input == null ? null : input.replace( 'e', PhoneNumberUtils.WAIT )
+ .replace( 'T', PhoneNumberUtils.PAUSE )
+ .replace( '?', PhoneNumberUtils.WILD );
+ }
+
+ /**
+ * convert the GSM pause/wild/wait character to the phone number in the SIM pb record format
+ */
+ private static String convertNumberToRecordFormat(String input) {
+ return input == null ? null : input.replace(PhoneNumberUtils.WAIT, 'e')
+ .replace(PhoneNumberUtils.PAUSE, 'T')
+ .replace(PhoneNumberUtils.WILD, '?');
+ }
+
+ private static String convertNullToEmptyString(String string) {
+ return string != null ? string : "";
+ }
+
+ public boolean isEmpty() {
+ return TextUtils.isEmpty(mAlphaTag)
+ && TextUtils.isEmpty(mNumber)
+ && ArrayUtils.isEmpty(mEmails)
+ && ArrayUtils.isEmpty(mAdditionalNumbers);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("SimPhoneBookRecord{").append("index =")
+ .append(mRecordIndex).append(", name = ")
+ .append(mAlphaTag == null ? "null" : mAlphaTag)
+ .append(", number = ").append(mNumber == null ? "null" : mNumber)
+ .append(", email count = ").append(mEmails == null ? 0 : mEmails.length)
+ .append(", email = ").append(Arrays.toString(mEmails))
+ .append(", ad number count = ")
+ .append(mAdditionalNumbers == null ? 0 : mAdditionalNumbers.length)
+ .append(", ad number = ").append(Arrays.toString(mAdditionalNumbers))
+ .append("}");
+ return sb.toString();
+ }
+
+ public final static class Builder {
+ private int mRecordIndex = 0;
+ private String mAlphaTag = null;
+ private String mNumber = null;
+ private String[] mEmails;
+ private String[] mAdditionalNumbers;
+
+ public SimPhonebookRecord build() {
+ SimPhonebookRecord record = new SimPhonebookRecord();
+ record.mAlphaTag = mAlphaTag;
+ record.mRecordIndex = mRecordIndex;
+ record.mNumber = mNumber;
+ if (mEmails != null) {
+ record.mEmails = mEmails;
+ }
+ if (mAdditionalNumbers != null) {
+ record.mAdditionalNumbers = mAdditionalNumbers;
+ }
+ return record;
+ }
+
+ public Builder setRecordIndex(int index) {
+ mRecordIndex = index;
+ return this;
+ }
+
+ public Builder setAlphaTag(String tag) {
+ mAlphaTag = tag;
+ return this;
+ }
+
+ public Builder setNumber(String number) {
+ mNumber = number;
+ return this;
+ }
+
+ public Builder setEmails(String[] emails) {
+ mEmails = emails;
+ return this;
+ }
+
+ public Builder setAdditionalNumbers(String[] anrs) {
+ mAdditionalNumbers = anrs;
+ return this;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/SimPhonebookRecordCache.java b/src/java/com/android/internal/telephony/uicc/SimPhonebookRecordCache.java
new file mode 100644
index 0000000..ca00112
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/SimPhonebookRecordCache.java
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) 2021 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.annotation.RequiresFeature;
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.RadioInterfaceCapabilityController;
+import com.android.internal.telephony.uicc.AdnCapacity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+/**
+ * Used to store SIM phonebook records.
+ * <p/>
+ * This will be {@link #INVALID} if either is the case:
+ * <ol>
+ * <li>The device does not support
+ * {@link android.telephony.TelephonyManager#CAPABILITY_SIM_PHONEBOOK_IN_MODEM}.</li>
+ * </ol>
+ * {@hide}
+ */
+@RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = "TelephonyManager.CAPABILITY_SIM_PHONEBOOK_IN_MODEM")
+public class SimPhonebookRecordCache extends Handler {
+ // Instance Variables
+ private static final String LOG_TAG = "SimPhonebookRecordCache";
+ private static final boolean DBG = true;
+ // Event Constants
+ private static final int EVENT_PHONEBOOK_CHANGED = 1;
+ private static final int EVENT_PHONEBOOK_RECORDS_RECEIVED = 2;
+ private static final int EVENT_GET_PHONEBOOK_RECORDS_DONE = 3;
+ private static final int EVENT_GET_PHONEBOOK_CAPACITY_DONE = 4;
+ private static final int EVENT_UPDATE_PHONEBOOK_RECORD_DONE = 5;
+ private static final int EVENT_SIM_REFRESH = 6;
+ private static final int EVENT_GET_PHONEBOOK_RECORDS_RETRY = 7;
+
+ private static final int MAX_RETRY_COUNT = 3;
+ private static final int RETRY_INTERVAL = 3000; // 3S
+
+ // member variables
+ private final CommandsInterface mCi;
+ private int mPhoneId;
+ private Context mContext;
+
+ // Presenting ADN capacity, including ADN, EMAIL ANR, and so on.
+ private AtomicReference<AdnCapacity> mAdnCapacity = new AtomicReference<AdnCapacity>(null);
+ private Object mReadLock = new Object();
+ private List<AdnRecord> mSimPbRecords =
+ Collections.synchronizedList(new ArrayList<AdnRecord>());
+ private List<UpdateRequest> mUpdateRequests =
+ Collections.synchronizedList(new ArrayList<UpdateRequest>());
+ // If true, clear the records in the cache and re-query from modem
+ private AtomicBoolean mIsCacheInvalidated = new AtomicBoolean(false);
+ private AtomicBoolean mIsRecordLoading = new AtomicBoolean(false);
+ private AtomicBoolean mIsInRetry = new AtomicBoolean(false);
+ private AtomicBoolean mIsInitialized = new AtomicBoolean(false);
+
+ // People waiting for SIM phonebook records to be loaded
+ ArrayList<Message> mAdnLoadingWaiters = new ArrayList<Message>();
+ /**
+ * The manual update from upper layer will result in notifying SIM phonebook changed,
+ * leading to fetch the Adn capacity, then whether to need to reload phonebook records
+ * is a problem. the SIM phoneback changed shall follow by updating record done, so that
+ * uses this flag to avoid unnecessary loading.
+ */
+ boolean mIsUpdateDone = false;
+
+ public SimPhonebookRecordCache(Context context, int phoneId, CommandsInterface ci) {
+ mCi = ci;
+ mPhoneId = phoneId;
+ mContext = context;
+ mCi.registerForSimPhonebookChanged(this, EVENT_PHONEBOOK_CHANGED, null);
+ mCi.registerForIccRefresh(this, EVENT_SIM_REFRESH, null);
+ mCi.registerForSimPhonebookRecordsReceived(this, EVENT_PHONEBOOK_RECORDS_RECEIVED, null);
+ }
+
+ /**
+ * This is recommended to use in work thread like IccPhoneBookInterfaceManager
+ * because it can't block main thread.
+ * @return true if this feature is supported
+ */
+ public boolean isEnabled() {
+ boolean isEnabled = RadioInterfaceCapabilityController
+ .getInstance()
+ .getCapabilities()
+ .contains(TelephonyManager.CAPABILITY_SIM_PHONEBOOK_IN_MODEM);
+ return mIsInitialized.get() || isEnabled;
+ }
+
+ public void dispose() {
+ reset();
+ mCi.unregisterForSimPhonebookChanged(this);
+ mCi.unregisterForIccRefresh(this);
+ mCi.unregisterForSimPhonebookRecordsReceived(this);
+ }
+
+ private void reset() {
+ mAdnCapacity.set(null);
+ mSimPbRecords.clear();
+ mIsCacheInvalidated.set(false);
+ mIsRecordLoading.set(false);
+ mIsInRetry.set(false);
+ mIsInitialized.set(false);
+ mIsUpdateDone = false;
+ }
+
+ private void sendErrorResponse(Message response, String errString) {
+ if (response != null) {
+ Exception e = new RuntimeException(errString);
+ AsyncResult.forMessage(response).exception = e;
+ response.sendToTarget();
+ }
+ }
+
+ private void notifyAndClearWaiters() {
+ synchronized (mReadLock) {
+ for (Message response : mAdnLoadingWaiters){
+ if (response != null) {
+ AsyncResult.forMessage(response).result = mSimPbRecords;
+ response.sendToTarget();
+ }
+ }
+ mAdnLoadingWaiters.clear();
+ }
+ }
+
+ private void sendResponsesToWaitersWithError() {
+ synchronized (mReadLock) {
+ mReadLock.notify();
+
+ for (Message response : mAdnLoadingWaiters) {
+ sendErrorResponse(response, "Query adn record failed");
+ }
+ mAdnLoadingWaiters.clear();
+ }
+ }
+
+ private void getSimPhonebookCapacity() {
+ logd("Start to getSimPhonebookCapacity");
+ mCi.getSimPhonebookCapacity(obtainMessage(EVENT_GET_PHONEBOOK_CAPACITY_DONE));
+ }
+
+ public AdnCapacity getAdnCapacity() {
+ return mAdnCapacity.get();
+ }
+
+ private void fillCache() {
+ synchronized (mReadLock) {
+ fillCacheWithoutWaiting();
+ try {
+ mReadLock.wait();
+ } catch (InterruptedException e) {
+ loge("Interrupted Exception in queryAdnRecord");
+ }
+ }
+ }
+
+ private void fillCacheWithoutWaiting() {
+ logd("Start to queryAdnRecord");
+ if (mIsRecordLoading.compareAndSet(false, true)) {
+ mCi.getSimPhonebookRecords(obtainMessage(EVENT_GET_PHONEBOOK_RECORDS_DONE));
+ } else {
+ logd("The loading is ongoing");
+ }
+ }
+
+ public void requestLoadAllPbRecords(Message response) {
+ if (response == null && !mIsInitialized.get()) {
+ logd("Try to enforce flushing cache");
+ fillCacheWithoutWaiting();
+ return;
+ }
+
+ synchronized (mReadLock) {
+ mAdnLoadingWaiters.add(response);
+ final int pendingSize = mAdnLoadingWaiters.size();
+ boolean isCapacityInvalid = getAdnCapacity() == null;
+ if (isCapacityInvalid) {
+ getSimPhonebookCapacity();
+ }
+ if (pendingSize > 1 || mIsInRetry.get()
+ || !mIsInitialized.get() || isCapacityInvalid) {
+ logd("Add to the pending list as pending size = "
+ + pendingSize + " is retrying = " + mIsInRetry.get()
+ + " IsInitialized = " + mIsInitialized.get());
+ return;
+ }
+ }
+ if (!mIsRecordLoading.get() && !mIsInRetry.get()) {
+ logd("ADN cache has already filled in");
+ if (!mIsCacheInvalidated.get()) {
+ notifyAndClearWaiters();
+ return;
+ }
+ }
+ fillCache();
+ }
+
+ @VisibleForTesting
+ public boolean isLoading() {
+ return mIsRecordLoading.get();
+ }
+
+ @VisibleForTesting
+ public List<AdnRecord> getAdnRecords() {
+ return mSimPbRecords;
+ }
+
+ private void notifyAdnLoadingWaiters() {
+ synchronized (mReadLock) {
+ mReadLock.notify();
+ }
+ notifyAndClearWaiters();
+ }
+
+ public void updateSimPbAdnByRecordId(int recordId, AdnRecord newAdn, Message response) {
+ if (newAdn == null) {
+ sendErrorResponse(response, "There is an invalid new Adn for update");
+ return;
+ }
+ boolean found = false;
+ int index = 0;
+ for (Iterator<AdnRecord> it = mSimPbRecords.iterator(); it.hasNext();) {
+ AdnRecord oldAdn = it.next();
+ ++index;
+ if (oldAdn.getRecId() == recordId) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ sendErrorResponse(response, "There is an invalid old Adn for update");
+ return;
+ }
+ updateSimPhonebookByNewAdn(index, newAdn, response);
+ }
+
+ public void updateSimPbAdnBySearch(AdnRecord oldAdn, AdnRecord newAdn, Message response) {
+ int index = -1;
+ if ((oldAdn == null || oldAdn.isEmpty()) && !newAdn.isEmpty()) {
+ // Add contact
+ index = 0;
+ } else {
+ int count = 1;
+ // Delete or update contact
+ for (Iterator<AdnRecord> it = mSimPbRecords.iterator(); it.hasNext();) {
+ if (oldAdn.isEqual(it.next())) {
+ index = count;
+ break;
+ }
+ count++;
+ }
+ }
+ if (index == -1) {
+ sendErrorResponse(response, "SIM Phonebook record don't exist for " + oldAdn);
+ return;
+ }
+
+ if (newAdn == null) {
+ sendErrorResponse(response, "There is an invalid new Adn for update");
+ return;
+ }
+
+ if (index == 0 && mAdnCapacity.get() != null && mAdnCapacity.get().isSimFull()) {
+ sendErrorResponse(response, "SIM Phonebook record is full");
+ return;
+ }
+
+ updateSimPhonebookByNewAdn(index, newAdn, response);
+ }
+
+ private void updateSimPhonebookByNewAdn(int index, AdnRecord newAdn, Message response) {
+ int recordIndex = (index == 0) ? newAdn.getRecId()
+ : mSimPbRecords.get(index - 1).getRecId();
+ SimPhonebookRecord updateAdn = new SimPhonebookRecord.Builder()
+ .setRecordIndex(recordIndex)
+ .setAlphaTag(newAdn.getAlphaTag())
+ .setNumber(newAdn.getNumber())
+ .setEmails(newAdn.getEmails())
+ .setAdditionalNumbers(newAdn.getAdditionalNumbers())
+ .build();
+ UpdateRequest updateRequest = new UpdateRequest(index, newAdn, updateAdn, response);
+ mUpdateRequests.add(updateRequest);
+ boolean isCapacityInvalid = getAdnCapacity() == null;
+ if (isCapacityInvalid) {
+ getSimPhonebookCapacity();
+ }
+ if (mIsRecordLoading.get() || mIsInRetry.get() || mUpdateRequests.size() > 1
+ || !mIsInitialized.get() || isCapacityInvalid) {
+ logd("It is pending on update as " + " mIsRecordLoading = " + mIsRecordLoading.get()
+ + " mIsInRetry = " + mIsInRetry.get() + " pending size = "
+ + mUpdateRequests.size() + " mIsInitialized = " + mIsInitialized.get());
+ return;
+ }
+
+ updateSimPhonebook(updateRequest);
+ }
+
+ private void updateSimPhonebook(UpdateRequest request) {
+ logd("update Sim phonebook");
+ mCi.updateSimPhonebookRecord(request.phonebookRecord,
+ obtainMessage(EVENT_UPDATE_PHONEBOOK_RECORD_DONE, request));
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ switch(msg.what) {
+ case EVENT_PHONEBOOK_CHANGED:
+ logd("EVENT_PHONEBOOK_CHANGED");
+ handlePhonebookChanged();
+ break;
+ case EVENT_GET_PHONEBOOK_RECORDS_DONE:
+ logd("EVENT_GET_PHONEBOOK_RECORDS_DONE");
+ ar = (AsyncResult)msg.obj;
+ if (ar != null && ar.exception != null) {
+ loge("Failed to gain phonebook records");
+ invalidateSimPbCache();
+ if (!mIsInRetry.get()) {
+ sendGettingPhonebookRecordsRetry(0);
+ }
+ }
+ break;
+ case EVENT_GET_PHONEBOOK_CAPACITY_DONE:
+ logd("EVENT_GET_PHONEBOOK_CAPACITY_DONE");
+ ar = (AsyncResult)msg.obj;
+ if (ar != null && ar.exception == null) {
+ AdnCapacity capacity = (AdnCapacity)ar.result;
+ handlePhonebookCapacityChanged(capacity);
+ }
+ break;
+ case EVENT_PHONEBOOK_RECORDS_RECEIVED:
+ logd("EVENT_PHONEBOOK_RECORDS_RECEIVED");
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception != null) {
+ loge("Unexpected exception happened");
+ ar.result = null;
+ }
+
+ handlePhonebookRecordReceived((ReceivedPhonebookRecords)(ar.result));
+ break;
+ case EVENT_UPDATE_PHONEBOOK_RECORD_DONE:
+ logd("EVENT_UPDATE_PHONEBOOK_RECORD_DONE");
+ ar = (AsyncResult)msg.obj;
+ handleUpdatePhonebookRecordDone(ar);
+ break;
+ case EVENT_SIM_REFRESH:
+ logd("EVENT_SIM_REFRESH");
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ handleSimRefresh((IccRefreshResponse)ar.result);
+ } else {
+ logd("SIM refresh Exception: " + ar.exception);
+ }
+ break;
+ case EVENT_GET_PHONEBOOK_RECORDS_RETRY:
+ int retryCount = msg.arg1;
+ logd("EVENT_GET_PHONEBOOK_RECORDS_RETRY cnt = " + retryCount);
+ if (retryCount < MAX_RETRY_COUNT) {
+ mIsRecordLoading.set(false);
+ fillCacheWithoutWaiting();
+ sendGettingPhonebookRecordsRetry(++retryCount);
+ } else {
+ responseToWaitersWithErrorOrSuccess(false);
+ }
+ break;
+ default:
+ loge("Unexpected event: " + msg.what);
+ }
+
+ }
+
+ private void responseToWaitersWithErrorOrSuccess(boolean success) {
+ logd("responseToWaitersWithErrorOrSuccess success = " + success);
+ mIsRecordLoading.set(false);
+ mIsInRetry.set(false);
+ if (success) {
+ notifyAdnLoadingWaiters();
+ } else {
+ sendResponsesToWaitersWithError();
+
+ }
+ tryFireUpdatePendingList();
+ }
+
+ private void handlePhonebookChanged() {
+ if (mUpdateRequests.isEmpty()) {
+ // If this event is received, means this feature is supported.
+ getSimPhonebookCapacity();
+ } else {
+ logd("Do nothing in the midst of multiple update");
+ }
+ }
+
+ private void handlePhonebookCapacityChanged(AdnCapacity newCapacity) {
+ AdnCapacity oldCapacity = mAdnCapacity.get();
+ mAdnCapacity.set(newCapacity);
+ if (oldCapacity == null && newCapacity != null) {
+ if (newCapacity.getMaxAdnCount() > 0){
+ mSimPbRecords.clear();
+ fillCacheWithoutWaiting();
+ } else {
+ notifyAdnLoadingWaiters();
+ }
+ mIsInitialized.set(true); // Let's say the whole process is ready
+ } else {
+ // There is nothing from PB, so notify waiters directly if any
+ if (newCapacity != null && newCapacity.getMaxAdnCount() == 0) {
+ notifyAdnLoadingWaiters();
+ } else if (!mIsUpdateDone) {
+ invalidateSimPbCache();
+ fillCacheWithoutWaiting();
+ }
+ mIsUpdateDone = false;
+ }
+ }
+
+ private void handlePhonebookRecordReceived(ReceivedPhonebookRecords records) {
+ if (records != null) {
+ if (records.isOk()) {
+ logd("Partial data is received");
+ populateAdnRecords(records.getPhonebookRecords());
+ } else if (records.isCompleted()) {
+ logd("The whole loading process is finished");
+ populateAdnRecords(records.getPhonebookRecords());
+ mIsRecordLoading.set(false);
+ mIsInRetry.set(false);
+ notifyAdnLoadingWaiters();
+ tryFireUpdatePendingList();
+ } else if (records.isRetryNeeded() && !mIsInRetry.get()) {
+ logd("Start to retry as aborted");
+ sendGettingPhonebookRecordsRetry(0);
+ } else {
+ loge("Error happened");
+ // Let's keep the stale data, in example of SIM getting removed during loading,
+ // expects to finish the whole process.
+ responseToWaitersWithErrorOrSuccess(true);
+ }
+ } else {
+ loge("No records there");
+ responseToWaitersWithErrorOrSuccess(true);
+ }
+ }
+
+ private void handleUpdatePhonebookRecordDone(AsyncResult ar) {
+ Exception e = null;
+ UpdateRequest updateRequest = (UpdateRequest)ar.userObj;
+ mIsUpdateDone = true;
+ if (ar.exception == null) {
+ int index = updateRequest.index;
+ AdnRecord adn = updateRequest.adnRecord;
+ int recordIndex = ((int[]) (ar.result))[0];
+
+ if (index == 0) {
+ // add contact
+ addSimPbRecord(adn, recordIndex);
+ } else if (adn.isEmpty()){
+ // delete contact
+ AdnRecord deletedRecord = mSimPbRecords.get(index - 1);
+ int adnRecordIndex = deletedRecord.getRecId();
+ logd("Record number for deleted ADN is " + adnRecordIndex);
+ if(recordIndex == adnRecordIndex) {
+ deleteSimPbRecord(index);
+ } else {
+ e = new RuntimeException(
+ "The index for deleted ADN record did not match");
+ }
+ } else {
+ // Change contact
+ if (mSimPbRecords.size() > index - 1) {
+ AdnRecord oldRecord = mSimPbRecords.get(index - 1);
+ int adnRecordIndex = oldRecord.getRecId();
+ logd("Record number for changed ADN is " + adnRecordIndex);
+ if(recordIndex == adnRecordIndex) {
+ updateSimPbRecord(adn, recordIndex, index);
+ } else {
+ e = new RuntimeException(
+ "The index for changed ADN record did not match");
+ }
+ } else {
+ e = new RuntimeException(
+ "The index for changed ADN record is out of the border");
+ }
+ }
+ } else {
+ e = new RuntimeException("Update adn record failed", ar.exception);
+ }
+
+ if (mUpdateRequests.contains(updateRequest)) {
+ mUpdateRequests.remove(updateRequest);
+ updateRequest.responseResult(e);
+ } else {
+ loge("this update request isn't found");
+ }
+ tryFireUpdatePendingList();
+ }
+
+ private void tryFireUpdatePendingList() {
+ if (!mUpdateRequests.isEmpty()) {
+ updateSimPhonebook(mUpdateRequests.get(0));
+ }
+ }
+
+ private void handleSimRefresh(IccRefreshResponse iccRefreshResponse) {
+ if (iccRefreshResponse != null) {
+ if (iccRefreshResponse.refreshResult == IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE
+ && (iccRefreshResponse.efId == IccConstants.EF_PBR ||
+ iccRefreshResponse.efId == IccConstants.EF_ADN) ||
+ iccRefreshResponse.refreshResult == IccRefreshResponse.REFRESH_RESULT_INIT) {
+ invalidateSimPbCache();
+ getSimPhonebookCapacity();
+ }
+ } else {
+ logd("IccRefreshResponse received is null");
+ }
+ }
+
+ private void populateAdnRecords(List<SimPhonebookRecord> records) {
+ if (records != null) {
+ List<AdnRecord> newRecords = records.stream().map(record -> {return
+ new AdnRecord(0, // PBR or ADN
+ record.getRecordIndex(),
+ record.getAlphaTag(),
+ record.getNumber(),
+ record.getEmails(),
+ record.getAdditionalNumbers());}).collect(Collectors.toList());
+ mSimPbRecords.addAll(newRecords);
+ }
+ }
+
+ private void sendGettingPhonebookRecordsRetry (int times) {
+ if (hasMessages(EVENT_GET_PHONEBOOK_RECORDS_RETRY)) {
+ removeMessages(EVENT_GET_PHONEBOOK_RECORDS_RETRY);
+ }
+ mIsInRetry.set(true);
+ Message message = obtainMessage(EVENT_GET_PHONEBOOK_RECORDS_RETRY, 1, 0);
+ sendMessageDelayed(message, RETRY_INTERVAL);
+ }
+
+ private void addSimPbRecord(AdnRecord addedRecord, int recordIndex) {
+ logd("Record number for the added ADN is " + recordIndex);
+ addedRecord.setRecId(recordIndex);
+ mSimPbRecords.add(addedRecord);
+ }
+
+
+ private void deleteSimPbRecord(int index) {
+ logd("Record number for the deleted ADN is " + index);
+ mSimPbRecords.remove(index - 1);
+ }
+
+ private void updateSimPbRecord(AdnRecord newRecord,
+ int recordIndex, int index) {
+ logd("Record number for the updated ADN is " + recordIndex);
+ newRecord.setRecId(recordIndex);
+ mSimPbRecords.set(index - 1, newRecord);
+ }
+
+ private void invalidateSimPbCache() {
+ logd("invalidateSimPbCache");
+ mIsCacheInvalidated.set(true);
+ mSimPbRecords.clear();
+ }
+
+ private void logd(String msg) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, msg);
+ }
+ }
+
+ private void loge(String msg) {
+ if (DBG) {
+ Rlog.e(LOG_TAG, msg);
+ }
+ }
+
+ private final static class UpdateRequest {
+ private int index;
+ private Message response;
+ private AdnRecord adnRecord;
+ private SimPhonebookRecord phonebookRecord;
+
+ UpdateRequest(int index, AdnRecord record, SimPhonebookRecord phonebookRecord,
+ Message response) {
+ this.index = index;
+ this.adnRecord = record;
+ this.phonebookRecord = phonebookRecord;
+ this.response = response;
+ }
+
+ void responseResult(Exception e) {
+ if (response != null) {
+ AsyncResult.forMessage(response, null, e);
+ response.sendToTarget();
+ }
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RadioConfigResponseTest.java b/tests/telephonytests/src/com/android/internal/telephony/RadioConfigResponseTest.java
index a346ece..f8a3330 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RadioConfigResponseTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RadioConfigResponseTest.java
@@ -48,7 +48,7 @@
assertFalse(
caps.contains(TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE));
assertFalse(
- caps.contains(TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED));
+ caps.contains(TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK));
assertFalse(
caps.contains(
TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE));
@@ -64,7 +64,7 @@
assertFalse(
caps.contains(TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE));
assertTrue(
- caps.contains(TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED));
+ caps.contains(TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK));
assertFalse(
caps.contains(
TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE));
@@ -80,7 +80,7 @@
assertTrue(
caps.contains(TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE));
assertTrue(
- caps.contains(TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED));
+ caps.contains(TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK));
assertTrue(
caps.contains(
TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimPhoneBookTest.java b/tests/telephonytests/src/com/android/internal/telephony/SimPhoneBookTest.java
index 15e16fb..ea6f19d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimPhoneBookTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimPhoneBookTest.java
@@ -16,9 +16,12 @@
package com.android.internal.telephony;
+import android.content.ContentValues;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.test.suitebuilder.annotation.Suppress;
+import com.android.internal.telephony.IccProvider;
import com.android.internal.telephony.uicc.AdnRecord;
import com.android.internal.telephony.uicc.IccConstants;
@@ -38,15 +41,17 @@
.get());
assertNotNull(simPhoneBook);
- int size[] = simPhoneBook.getAdnRecordsSize(IccConstants.EF_ADN);
+ final int subId = SubscriptionManager.getDefaultSubscriptionId();
+ int size[] = simPhoneBook.getAdnRecordsSizeForSubscriber(subId, IccConstants.EF_ADN);
assertNotNull(size);
assertEquals(3, size.length);
assertEquals(size[0] * size[2], size[1]);
assertTrue(size[2] >= 100);
- List<AdnRecord> adnRecordList = simPhoneBook.getAdnRecordsInEf(IccConstants.EF_ADN);
+ List<AdnRecord> adnRecordList =
+ simPhoneBook.getAdnRecordsInEfForSubscriber(subId, IccConstants.EF_ADN);
// do it twice cause the second time shall read from cache only
- adnRecordList = simPhoneBook.getAdnRecordsInEf(IccConstants.EF_ADN);
+ adnRecordList = simPhoneBook.getAdnRecordsInEfForSubscriber(subId, IccConstants.EF_ADN);
assertNotNull(adnRecordList);
// Test for phone book update
@@ -75,37 +80,66 @@
String pin2 = null;
// udpate by index
- boolean success = simPhoneBook.updateAdnRecordsInEfByIndex(IccConstants.EF_ADN,
- firstAdn.getAlphaTag(), firstAdn.getNumber(), adnIndex, pin2);
- adnRecordList = simPhoneBook.getAdnRecordsInEf(IccConstants.EF_ADN);
- AdnRecord tmpAdn = adnRecordList.get(listIndex);
+ ContentValues values = new ContentValues();
+ values.put(IccProvider.STR_NEW_TAG, firstAdn.getAlphaTag());
+ values.put(IccProvider.STR_NEW_NUMBER, firstAdn.getNumber());
+ values.put(IccProvider.STR_NEW_EMAILS, "");
+ values.put(IccProvider.STR_NEW_ANRS, "");
+ boolean success = simPhoneBook.updateAdnRecordsInEfByIndexForSubscriber(subId,
+ IccConstants.EF_ADN, values, adnIndex, pin2);
+ adnRecordList =
+ simPhoneBook.getAdnRecordsInEfForSubscriber(subId, IccConstants.EF_ADN);
+ AdnRecord tmpAdn = adnRecordList.get(listIndex);
assertTrue(success);
assertTrue(firstAdn.isEqual(tmpAdn));
// replace by search
- success = simPhoneBook.updateAdnRecordsInEfBySearch(IccConstants.EF_ADN,
- firstAdn.getAlphaTag(), firstAdn.getNumber(),
- secondAdn.getAlphaTag(), secondAdn.getNumber(), pin2);
- adnRecordList = simPhoneBook.getAdnRecordsInEf(IccConstants.EF_ADN);
+ ContentValues values2 = new ContentValues();
+ values.put(IccProvider.STR_TAG, firstAdn.getAlphaTag());
+ values.put(IccProvider.STR_NUMBER, firstAdn.getNumber());
+ values.put(IccProvider.STR_EMAILS, "");
+ values.put(IccProvider.STR_ANRS, "");
+ values.put(IccProvider.STR_NEW_TAG, secondAdn.getAlphaTag());
+ values.put(IccProvider.STR_NEW_NUMBER, secondAdn.getNumber());
+ values.put(IccProvider.STR_NEW_EMAILS, "");
+ values.put(IccProvider.STR_NEW_ANRS, "");
+ success = simPhoneBook.updateAdnRecordsInEfBySearchForSubscriber(subId,
+ IccConstants.EF_ADN, values2, pin2);
+ adnRecordList =
+ simPhoneBook.getAdnRecordsInEfForSubscriber(subId, IccConstants.EF_ADN);
tmpAdn = adnRecordList.get(listIndex);
assertTrue(success);
assertFalse(firstAdn.isEqual(tmpAdn));
assertTrue(secondAdn.isEqual(tmpAdn));
// erase be search
- success = simPhoneBook.updateAdnRecordsInEfBySearch(IccConstants.EF_ADN,
- secondAdn.getAlphaTag(), secondAdn.getNumber(),
- emptyAdn.getAlphaTag(), emptyAdn.getNumber(), pin2);
- adnRecordList = simPhoneBook.getAdnRecordsInEf(IccConstants.EF_ADN);
+ ContentValues values3 = new ContentValues();
+ values.put(IccProvider.STR_TAG, secondAdn.getAlphaTag());
+ values.put(IccProvider.STR_NUMBER, secondAdn.getNumber());
+ values.put(IccProvider.STR_EMAILS, "");
+ values.put(IccProvider.STR_ANRS, "");
+ values.put(IccProvider.STR_NEW_TAG, emptyAdn.getAlphaTag());
+ values.put(IccProvider.STR_NEW_NUMBER, emptyAdn.getNumber());
+ values.put(IccProvider.STR_NEW_EMAILS, "");
+ values.put(IccProvider.STR_NEW_ANRS, "");
+ success = simPhoneBook.updateAdnRecordsInEfBySearchForSubscriber(subId,
+ IccConstants.EF_ADN, values3, pin2);
+ adnRecordList =
+ simPhoneBook.getAdnRecordsInEfForSubscriber(subId, IccConstants.EF_ADN);
tmpAdn = adnRecordList.get(listIndex);
assertTrue(success);
assertTrue(tmpAdn.isEmpty());
// restore the orginial adn
- success = simPhoneBook.updateAdnRecordsInEfByIndex(IccConstants.EF_ADN,
- originalAdn.getAlphaTag(), originalAdn.getNumber(), adnIndex,
- pin2);
- adnRecordList = simPhoneBook.getAdnRecordsInEf(IccConstants.EF_ADN);
+ ContentValues values4 = new ContentValues();
+ values.put(IccProvider.STR_NEW_TAG, originalAdn.getAlphaTag());
+ values.put(IccProvider.STR_NEW_NUMBER, originalAdn.getNumber());
+ values.put(IccProvider.STR_NEW_EMAILS, "");
+ values.put(IccProvider.STR_NEW_ANRS, "");
+ success = simPhoneBook.updateAdnRecordsInEfByIndexForSubscriber(subId,
+ IccConstants.EF_ADN, values4, adnIndex, pin2);
+ adnRecordList =
+ simPhoneBook.getAdnRecordsInEfForSubscriber(subId, IccConstants.EF_ADN);
tmpAdn = adnRecordList.get(listIndex);
assertTrue(success);
assertTrue(originalAdn.isEqual(tmpAdn));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
index 949fca7..c545cdd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
@@ -68,6 +68,9 @@
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SuppServiceNotification;
+import com.android.internal.telephony.uicc.AdnCapacity;
+import com.android.internal.telephony.uicc.ReceivedPhonebookRecords;
+import com.android.internal.telephony.uicc.SimPhonebookRecord;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccIoResult;
@@ -2453,4 +2456,30 @@
mVoiceRegStateResult = regStateResult;
}
+ @Override
+ public void getSimPhonebookRecords(Message result) {
+ resultSuccess(result, null);
+
+ // send a fake result
+ List<SimPhonebookRecord> phonebookRecordInfoGroup = new ArrayList<SimPhonebookRecord>();
+ mSimPhonebookRecordsReceivedRegistrants.notifyRegistrants(
+ new AsyncResult(null,
+ new ReceivedPhonebookRecords(4, phonebookRecordInfoGroup), null));
+ }
+
+ @Override
+ public void getSimPhonebookCapacity(Message result) {
+ resultSuccess(result, new AdnCapacity(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
+ }
+
+ @Override
+ public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) {
+ resultSuccess(result, new int[]{phonebookRecord.getRecordIndex()});
+ notifySimPhonebookChanged();
+ }
+
+ @VisibleForTesting
+ public void notifySimPhonebookChanged() {
+ mSimPhonebookChangedRegistrants.notifyRegistrants();
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
index c4d54ff..e9c74ae 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
@@ -36,6 +36,7 @@
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
+import com.android.internal.telephony.uicc.SimPhonebookRecord;
public class SimulatedCommandsVerifier implements CommandsInterface {
private static SimulatedCommandsVerifier sInstance;
@@ -1496,4 +1497,32 @@
@Override
public void getSlicingConfig(Message result) {
}
+
+ @Override
+ public void getSimPhonebookRecords(Message result){
+ }
+
+ @Override
+ public void getSimPhonebookCapacity(Message result){
+ }
+
+ @Override
+ public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result){
+ }
+
+ @Override
+ public void registerForSimPhonebookChanged(Handler h, int what, Object obj){
+ }
+
+ @Override
+ public void unregisterForSimPhonebookChanged(Handler h){
+ }
+
+ @Override
+ public void registerForSimPhonebookRecordsReceived(Handler h, int what, Object obj){
+ }
+
+ @Override
+ public void unregisterForSimPhonebookRecordsReceived(Handler h){
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
index 2f355bb..4ba26d1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -194,6 +194,182 @@
}
/**
+ * Add a device ImsService and ensure that querying the configured ImsService for all features
+ * reports the device ImsService.
+ */
+ @Test
+ @SmallTest
+ public void testGetConfiguredImsServiceDevice() throws Exception {
+ setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+ TEST_DEVICE_DEFAULT_NAME.getPackageName());
+ HashSet<String> features = new HashSet<>();
+ features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_RCS_FEATURE);
+ setupPackageQuery(TEST_DEVICE_DEFAULT_NAME, features, true);
+ setupController();
+
+ // Complete package manager lookup and cache.
+ startBindCarrierConfigAlreadySet();
+
+ // device package name should be returned for both features.
+ final String[] packageName = new String[1];
+ // Calling this method will block us until the looper processes the command, so use
+ // runWithLooper to allow the message to be processed.
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_MMTEL));
+ assertEquals(TEST_DEVICE_DEFAULT_NAME.getPackageName(), packageName[0]);
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_RCS));
+ assertEquals(TEST_DEVICE_DEFAULT_NAME.getPackageName(), packageName[0]);
+ }
+
+ /**
+ * Add in the case that there is no device or carrier ImsService found, we return null for
+ * configuration queries.
+ */
+ @Test
+ @SmallTest
+ public void testGetConfiguredImsServiceNoDeviceOrCarrier() throws Exception {
+ setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+ TEST_DEVICE_DEFAULT_NAME.getPackageName());
+ // package query returns null
+ setupController();
+ // Complete package manager lookup and cache.
+ startBindCarrierConfigAlreadySet();
+
+ // device package name should be returned for both features.
+ final String[] packageName = new String[1];
+ // Calling this method will block us until the looper processes the command, so use
+ // runWithLooper to allow the message to be processed.
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_MMTEL));
+ assertNull(packageName[0]);
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_RCS));
+ assertNull(packageName[0]);
+ }
+
+ /**
+ * Add in the case that there is no device or carrier ImsService configured, we return null for
+ * configuration queries.
+ */
+ @Test
+ @SmallTest
+ public void testGetConfiguredImsServiceNoDeviceConfig() throws Exception {
+ // device configuration for MMTEL and RCS is null
+ setupResolver(1 /*numSlots*/, null, null);
+ HashSet<String> features = new HashSet<>();
+ features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_RCS_FEATURE);
+ // ImsService query does report a device ImsService
+ setupPackageQuery(TEST_DEVICE_DEFAULT_NAME, features, true);
+ setupController();
+ // Complete package manager lookup and cache.
+ startBindCarrierConfigAlreadySet();
+
+ // device package name should be returned for both features.
+ final String[] packageName = new String[1];
+ // Calling this method will block us until the looper processes the command, so use
+ // runWithLooper to allow the message to be processed.
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_MMTEL));
+ assertNull(packageName[0]);
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_RCS));
+ assertNull(packageName[0]);
+ }
+
+ /**
+ * Add a device and carrier ImsService and ensure that querying the configured ImsService for
+ * all features reports the carrier ImsService and not device.
+ */
+ @Test
+ @SmallTest
+ public void testGetConfiguredImsServiceCarrier() throws Exception {
+ setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+ TEST_DEVICE_DEFAULT_NAME.getPackageName());
+ HashSet<String> features = new HashSet<>();
+ features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_RCS_FEATURE);
+ setConfigCarrierStringMmTelRcs(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ List<ResolveInfo> info = new ArrayList<>();
+ info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+ setupPackageQuery(info);
+ setupController();
+
+ // Complete package manager lookup and cache.
+ startBindCarrierConfigAlreadySet();
+ // Setup the carrier features and response.
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL));
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_RCS));
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
+
+ // carrier package name should be returned for both features.
+ final String[] packageName = new String[1];
+ // Calling this method will block us until the looper processes the command, so use
+ // runWithLooper to allow the message to be processed.
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_MMTEL));
+ assertEquals(TEST_CARRIER_DEFAULT_NAME.getPackageName(), packageName[0]);
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_RCS));
+ assertEquals(TEST_CARRIER_DEFAULT_NAME.getPackageName(), packageName[0]);
+ }
+
+ /**
+ * Add a device ImsService and ensure that querying the configured ImsService for all features
+ * reports the device ImsService though there is a configuration for carrier (but no cached
+ * ImsService).
+ */
+ @Test
+ @SmallTest
+ public void testGetConfiguredImsServiceCarrierDevice() throws Exception {
+ setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+ TEST_DEVICE_DEFAULT_NAME.getPackageName());
+ HashSet<String> features = new HashSet<>();
+ features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_RCS_FEATURE);
+ // Carrier service is configured
+ setConfigCarrierStringMmTelRcs(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ List<ResolveInfo> info = new ArrayList<>();
+ // Carrier ImsService is not found during package query.
+ info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
+ setupPackageQuery(info);
+ setupController();
+
+ // Complete package manager lookup and cache.
+ startBindCarrierConfigAlreadySet();
+
+ final String[] packageName = new String[1];
+ // Calling this method will block us until the looper processes the command, so use
+ // runWithLooper to allow the message to be processed.
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_MMTEL));
+ assertEquals(TEST_DEVICE_DEFAULT_NAME.getPackageName(), packageName[0]);
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_RCS));
+ assertEquals(TEST_DEVICE_DEFAULT_NAME.getPackageName(), packageName[0]);
+ }
+
+ /**
* Set the carrier config override value and ensure that ImsResolver calls .bind on that
* package name with the correct ImsFeatures.
*/
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java
old mode 100644
new mode 100755
index 72ff321..47ebc67
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java
@@ -15,18 +15,25 @@
*/
package com.android.internal.telephony.uicc;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.os.AsyncResult;
-
-import com.android.internal.telephony.IccPhoneBookInterfaceManager;
-import com.android.internal.telephony.TelephonyTest;
-
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
+import android.content.ContentValues;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.AsyncResult;
import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.IccPhoneBookInterfaceManager;
+import com.android.internal.telephony.IccProvider;
+import com.android.internal.telephony.TelephonyTest;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -34,10 +41,6 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.anyInt;
-
import java.util.Arrays;
import java.util.List;
@@ -47,6 +50,8 @@
private AdnRecordCache mAdnRecordCache;
@Mock
private AdnRecord mAdnRecord;
+ @Mock
+ private SimPhonebookRecordCache mSimPhonebookRecordCache;
private IccPhoneBookInterfaceManager mIccPhoneBookInterfaceMgr;
private IccPhoneBookInterfaceManagerHandler mIccPhoneBookInterfaceManagerHandler;
private List<AdnRecord> mAdnList = Arrays.asList(mAdnRecord);
@@ -82,9 +87,19 @@
}
}).when(mAdnRecordCache).requestLoadAllAdnLike(anyInt(), anyInt(), (Message) anyObject());
+ doAnswer(invocation -> {
+ Message response = (Message) invocation.getArguments()[0];
+ //set result for load ADN EF
+ AsyncResult.forMessage(response).result = mAdnList;
+ response.sendToTarget();
+ return null;
+ }).when(mSimPhonebookRecordCache).requestLoadAllPbRecords((Message)anyObject());
mIccPhoneBookInterfaceManagerHandler = new IccPhoneBookInterfaceManagerHandler(TAG);
mIccPhoneBookInterfaceManagerHandler.start();
+
waitUntilReady();
+ replaceInstance(IccPhoneBookInterfaceManager.class,
+ "mSimPbRecordCache", mIccPhoneBookInterfaceMgr, mSimPhonebookRecordCache);
}
@After
@@ -93,9 +108,11 @@
mIccPhoneBookInterfaceManagerHandler.join();
super.tearDown();
}
+
@Test
@SmallTest
public void testAdnEFLoadWithFailure() {
+ doReturn(false).when(mSimPhonebookRecordCache).isEnabled();
List<AdnRecord> adnListResult = mIccPhoneBookInterfaceMgr.getAdnRecordsInEf(
IccConstants.EF_ADN);
assertEquals(mAdnList, adnListResult);
@@ -116,4 +133,88 @@
//verify the previous read is not got affected
assertEquals(mAdnList, adnListResult);
}
+
+ @Test
+ @SmallTest
+ public void testAdnEFLoadByPbCacheWithFailure() {
+ doReturn(true).when(mSimPhonebookRecordCache).isEnabled();
+ List<AdnRecord> adnListResult = mIccPhoneBookInterfaceMgr.getAdnRecordsInEf(
+ IccConstants.EF_ADN);
+ assertEquals(mAdnList, adnListResult);
+ //mock a ADN Ef load failure
+ doAnswer(invocation -> {
+ Message response = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(response).exception = new RuntimeException();
+ response.sendToTarget();
+ return null;
+ }).when(mSimPhonebookRecordCache).requestLoadAllPbRecords((Message) anyObject());
+ List<AdnRecord> adnListResultNew = mIccPhoneBookInterfaceMgr.getAdnRecordsInEf(
+ IccConstants.EF_ADN);
+ //the later read return null due to exception
+ assertNull(adnListResultNew);
+ //verify the previous read is not got affected
+ assertEquals(mAdnList, adnListResult);
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateAdnRecord() {
+ doReturn(false).when(mSimPhonebookRecordCache).isEnabled();
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message response = (Message) invocation.getArguments()[4];
+ //set result for update ADN EF
+ AsyncResult.forMessage(response).exception = null;
+ response.sendToTarget();
+ return null;
+ }
+ }).when(mAdnRecordCache).updateAdnBySearch(
+ anyInt(), any(), any(),
+ any(), (Message) anyObject());
+
+ ContentValues values = new ContentValues();
+ values.put(IccProvider.STR_TAG, "");
+ values.put(IccProvider.STR_NUMBER, "");
+ values.put(IccProvider.STR_EMAILS, "");
+ values.put(IccProvider.STR_ANRS, "");
+ values.put(IccProvider.STR_NEW_TAG, "test");
+ values.put(IccProvider.STR_NEW_NUMBER, "123456");
+ values.put(IccProvider.STR_NEW_EMAILS, "");
+ values.put(IccProvider.STR_NEW_ANRS, "");
+
+ boolean result = mIccPhoneBookInterfaceMgr.updateAdnRecordsInEfBySearchForSubscriber(
+ IccConstants.EF_ADN, values , null);
+
+ assertTrue(result);
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateAdnRecordByPbCache() {
+ doReturn(true).when(mSimPhonebookRecordCache).isEnabled();
+ doAnswer(invocation -> {
+ Message response = (Message) invocation.getArguments()[2];
+ //set result for update ADN EF
+ AsyncResult.forMessage(response).exception = null;
+ response.sendToTarget();
+ return null;
+ }).when(mSimPhonebookRecordCache).updateSimPbAdnBySearch(any(),
+ any(), (Message) anyObject());
+
+ ContentValues values = new ContentValues();
+ values.put(IccProvider.STR_TAG, "");
+ values.put(IccProvider.STR_NUMBER, "");
+ values.put(IccProvider.STR_EMAILS, "");
+ values.put(IccProvider.STR_ANRS, "");
+ values.put(IccProvider.STR_NEW_TAG, "test");
+ values.put(IccProvider.STR_NEW_NUMBER, "123456");
+ values.put(IccProvider.STR_NEW_EMAILS, "");
+ values.put(IccProvider.STR_NEW_ANRS, "");
+
+ boolean result = mIccPhoneBookInterfaceMgr.updateAdnRecordsInEfBySearchForSubscriber(
+ IccConstants.EF_ADN, values , "12");
+
+ assertTrue(result);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/SimPhonebookRecordCacheTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/SimPhonebookRecordCacheTest.java
new file mode 100644
index 0000000..24db77d
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/SimPhonebookRecordCacheTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2021 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.AsyncResult;
+import android.os.HandlerThread;
+import android.os.Handler;
+import android.os.Message;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SimPhonebookRecordCacheTest extends TelephonyTest {
+ private static final int EVENT_PHONEBOOK_RECORDS_RECEIVED = 2;
+
+ private SimPhonebookRecordCache mSimPhonebookRecordCacheUt;
+ private SimPhonebookRecordHandler mSimPhonebookRecordHandler;
+
+ private class SimPhonebookRecordHandler extends HandlerThread {
+ SimPhonebookRecordHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mSimPhonebookRecordCacheUt =
+ new SimPhonebookRecordCache(mContext, 0, mSimulatedCommands);
+ setReady(true);
+ }
+
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(this.getClass().getSimpleName());
+ mSimPhonebookRecordHandler = new SimPhonebookRecordHandler(this.getClass().getSimpleName());
+ mSimPhonebookRecordHandler.start();
+ waitUntilReady();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mSimPhonebookRecordHandler.quit();
+ mSimPhonebookRecordHandler.join();
+ super.tearDown();
+ }
+
+ @Test
+ public void testSimPhonebookChangedOnBootup() {
+ mSimulatedCommands.notifySimPhonebookChanged();
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ AdnCapacity capacity = mSimPhonebookRecordCacheUt.getAdnCapacity();
+ AdnCapacity capVerifer = new AdnCapacity(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ assertNotNull(capacity);
+ assertTrue(capVerifer.equals(capacity));
+ mSimulatedCommands.notifySimPhonebookChanged();
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ assertTrue(capacity != mSimPhonebookRecordCacheUt.getAdnCapacity());
+ assertTrue(capVerifer.equals(capacity));
+ }
+
+ @Test
+ public void testGetPhonebookRecords() {
+ assertFalse(mSimPhonebookRecordCacheUt.isLoading());
+ mSimulatedCommands.notifySimPhonebookChanged();
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ assertFalse(mSimPhonebookRecordCacheUt.isLoading());
+ mSimPhonebookRecordCacheUt.requestLoadAllPbRecords(null);
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+
+ List<SimPhonebookRecord> records = new ArrayList<SimPhonebookRecord>();
+ records.add(new SimPhonebookRecord(10, "ABC", "12345", null, null));
+ AsyncResult ar = new AsyncResult(null, new ReceivedPhonebookRecords(4, records), null);
+ Message msg = Message.obtain(mSimPhonebookRecordCacheUt,
+ EVENT_PHONEBOOK_RECORDS_RECEIVED, ar);
+ mSimPhonebookRecordCacheUt.handleMessage(msg);
+
+ assertFalse(mSimPhonebookRecordCacheUt.isLoading());
+ List<AdnRecord> adnRecords = mSimPhonebookRecordCacheUt.getAdnRecords();
+ assertEquals(adnRecords.size(), 1);
+ assertEquals(adnRecords.get(0).getRecId(), 10);
+ }
+
+ @Test
+ public void testGetPhonebookRecordsWithoutInitization() {
+ assertFalse(mSimPhonebookRecordCacheUt.isLoading());
+ mSimPhonebookRecordCacheUt.requestLoadAllPbRecords(null);
+ assertTrue(mSimPhonebookRecordCacheUt.isLoading());
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ assertFalse(mSimPhonebookRecordCacheUt.isLoading());
+ }
+
+ @Test
+ public void testUpdatePhonebookRecord() {
+ List<AdnRecord> adnRecords = mSimPhonebookRecordCacheUt.getAdnRecords();
+ assertEquals(adnRecords.size(), 0);
+
+ AdnRecord newAdn = new AdnRecord(0, 20, "AB", "123", null, null);
+ // add
+ mSimPhonebookRecordCacheUt.updateSimPbAdnBySearch(null, newAdn, null);
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ adnRecords = mSimPhonebookRecordCacheUt.getAdnRecords();
+ assertEquals(adnRecords.size(), 1);
+ AdnRecord oldAdn = adnRecords.get(0);
+ assertEquals(oldAdn.getRecId(), 20);
+ assertEquals(oldAdn.getAlphaTag(), "AB");
+ assertEquals(oldAdn.getNumber(), "123");
+ // update
+ newAdn = new AdnRecord(0, 20, "ABCD", "123456789", null, null);
+ mSimPhonebookRecordCacheUt.updateSimPbAdnBySearch(oldAdn, newAdn, null);
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ adnRecords = mSimPhonebookRecordCacheUt.getAdnRecords();
+ assertEquals(adnRecords.size(), 1);
+ oldAdn = adnRecords.get(0);
+ assertEquals(oldAdn.getRecId(), 20);
+ assertEquals(oldAdn.getAlphaTag(), "ABCD");
+ assertEquals(oldAdn.getNumber(), "123456789");
+ // Delete
+ newAdn = new AdnRecord(0, 20, null, null, null, null);
+ mSimPhonebookRecordCacheUt.updateSimPbAdnBySearch(oldAdn, newAdn, null);
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ adnRecords = mSimPhonebookRecordCacheUt.getAdnRecords();
+ assertEquals(adnRecords.size(), 0);
+ }
+}