Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ba92f404 authored by Mengjun Leng's avatar Mengjun Leng Committed by Sarah Chin
Browse files

SimPhoneBook: Add ANR/EMAIL support for USIM phonebook

1. Add multi-EF_ANR/EF_EMAIL support for each ADN record.
2. Add the related interfaces for getting count of ADN, ANR, EMAIL.
    - getAdnRecordsCapacity
3. Use updateIccRecordInEf for all update/insert/delete operations.
    - add updateAdnRecordsWithContentValuesInEfBySearchUsingSubId for
      updating IccRecord

Bug: 23044962
Change-Id: Ic71fbc748364efbee5bad629b94c5a24865064b3
Merged-In: Ic71fbc748364efbee5bad629b94c5a24865064b3
parent ed644327
Loading
Loading
Loading
Loading
+17 −40
Original line number Diff line number Diff line
@@ -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 @@ interface IIccPhoneBook {
            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 @@ interface IIccPhoneBook {
     * 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 @@ interface IIccPhoneBook {
    @UnsupportedAppUsage
    int[] getAdnRecordsSizeForSubscriber(int subId, int efid);

    /**
     * Get the capacity of ADN records
     *
     * @param subId user preferred subId
     * @return AdnCapacity
     */
    AdnCapacity getAdnRecordsCapacityForSubscriber(int subId);
}
+71 −36
Original line number Diff line number Diff line
@@ -17,13 +17,16 @@
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;
@@ -141,53 +144,69 @@ public class IccPhoneBookInterfaceManager {
        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) {
            checkThread();
            Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, updateRequest);
            AdnRecord oldAdn = new AdnRecord(oldTag, oldPhoneNumber);
            AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
            AdnRecord oldAdn = generateAdnRecordWithOldTagByContentValues(values);
            AdnRecord newAdn = generateAdnRecordWithNewTagByContentValues(values);
            if (mAdnCache != null) {
                mAdnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response);
                waitForResult(updateRequest);
@@ -210,15 +229,14 @@ public class IccPhoneBookInterfaceManager {
     * @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)
@@ -227,17 +245,16 @@ public class IccPhoneBookInterfaceManager {
                    "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));

        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);
            AdnRecord newAdn = generateAdnRecordWithNewTagByContentValues(values);
            if (mAdnCache != null) {
                mAdnCache.updateAdnByIndex(efid, newAdn, index, pin2, response);
                waitForResult(updateRequest);
@@ -344,5 +361,23 @@ public class IccPhoneBookInterfaceManager {
        }
        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" );
        return null;
    }
}
+67 −97
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.telephony.SubscriptionManager;
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 @@ public class IccProvider extends ContentProvider {
        "name",
        "number",
        "emails",
        "anrs",
        "_id"
    };

@@ -62,10 +64,24 @@ public class IccProvider extends ContentProvider {
    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 @@ public class IccProvider extends ContentProvider {
                        "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 @@ public class IccProvider extends ContentProvider {
        // 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 @@ public class IccProvider extends ContentProvider {
            } 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 @@ public class IccProvider extends ContentProvider {
                        "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,48 +459,10 @@ public class IccProvider extends ContentProvider {
    }

    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.

        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("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;

        if (DBG) log("updateIccRecordInEf: efType=" + efType +
                ", values: [ "+ values + "  ], subId:" + subId);
        try {
            IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
                    TelephonyFrameworkInitializer
@@ -484,8 +470,9 @@ public class IccProvider extends ContentProvider {
                            .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 @@ public class IccProvider extends ContentProvider {
        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 @@ public class IccProvider extends ContentProvider {
    @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 @@ public class IccProvider extends ContentProvider {
                }
                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);
        }
    }
+42 −32

File changed.

Preview size limit exceeded, changes collapsed.

+19 −0
Original line number Diff line number Diff line
/*
 * 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;
Loading