instructions

The article is not finished yet, the specific call of irradio.iccioforapp method in the last step has not been found for the time being, if you can supplement it, thank you very much

reference

  1. Subid and Slotid_Firedancer0089 column -CSDN blog
  2. Android: All about ContentProvider knowledge here! _ Focus on sharing Android development dry goods -CSDN blog _contentProvider
  3. ADN loading process _Android development – UCloud Cloud community
  4. Android: comprehensive & detailed analytical Android 10 RSSI signal display and refresh the second, RIL, key classes and files: RIL. Java, ril_service. CPP, radioResponse. Java_GentelmanTsao blog – CSDN

preface

Recently, I received a bug with too long name and failed to export SIM. I have learned about SIM card storage and the process of importing Android device contacts into SIM

SIM storage Limits

(SIM standard Chinese should be only 4)

  1. SIM card record name is 14, the maximum length of 6, can input in Chinese (11 also can input in Chinese, such as every day every day every day every day every day, but as long as there is a different kind of Chinese can only have 6)
  2. The number length is too long

Mobile Phone Number Import SIM Card Process Record (MTK)

1. Application layer invocation

Mtk SimContacts application code simContactDaoimp.java


class SimContactDaoImpl extends SimContactDao{...public static final Uri ICC_CONTENT_URI = Uri.parse("content://icc/adn");
    public static String NUMBER = "number";
    public static String EMAILS = "emails";
    public static String ANRS = "anrs";
    private static final String STR_TAG = "tag";
    @Override
    public Uri createNewSimContact(SimContact simContact, int subscriptionId) {
        / / code 1
        Uri uri = ICC_CONTENT_URI.buildUpon()
                .appendPath("subId")
                .appendPath(String.valueOf(subscriptionId))
                .build();
        ContentValues values = new ContentValues();
        values.put(STR_TAG, TextUtils.isEmpty(simContact.getName()) ? "" : simContact.getName());
        if(! TextUtils.isEmpty(simContact.getPhone())) { values.put(NUMBER, PhoneNumberUtils.stripSeparators(simContact.getPhone())); } values.put(EMAILS, simContact.getEmailsString()); values.put(ANRS, simContact.getAnrsString(). replaceAll("[^0123456789PWN\\,\\;\\*\\#\\+\\:]".""));
        return mResolver.insert(uri,values);/ / call}... }Copy the code
  • As you can see from the above code, the action on the SIM card is to issue a Uri throughContentProviderThe process communicates and changes THE SIM information. The STR_TAG, NUMBER, EMAILS, ANRS, and URI fields are passed insubId.

Note: subId is the primary key value of the card in the database. You can query the corresponding card slot through subId.

IccProvider

Corresponding ContentProvider ICC_CONTENT_URI IccProvider (path: / frameworks/opt/telephony/SRC/Java/com/android/internal/telephony/Icc Provider.java)

IccProvider#insert

public Uri insert(Uri url, ContentValues initialValues) {
    Uri resultUri;
    int efType;
    String pin2 = null;
    int subId;
    int match = URL_MATCHER.match(url);
    switch (match) {
        ...
        case ADN_SUB:
                efType = IccConstants.EF_ADN;
                subId = getRequestSubId(url);
                break; . } String tag = initialValues.getAsString("tag");
    String number = initialValues.getAsString("number");
    // TODO(): Read email instead of sending null.
    // Code 1 calls addIccRecordToEf to add data to the SIM card
    boolean success = addIccRecordToEf(efType, tag, number, null, pin2, subId);

    if(! success) {return null;
    }

    StringBuilder buf = new StringBuilder("content://icc/");
    switch (match) {
        ...
        case ADN_SUB:
            buf.append("adn/subId/");
            break; . }// TODO: we need to find out the rowId for the newly added record
    buf.append(0);

    resultUri = Uri.parse(buf.toString());

    getContext().getContentResolver().notifyChange(url, null);
    /* // notify interested parties that an insertion happened getContext().getContentResolver().notifyInsert( resultUri, rowID, null); * /

    return resultUri;
}
Copy the code
  • Called in code 1addIccRecordToEfMethod to add data to a SIM card

IccProvider#addIccRecordToEf

private boolean
    addIccRecordToEf(int efType, String name, String number, String[] emails,
            String pin2, int subId) {...boolean success = false; .try {
            / / code 1
            IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
                    ServiceManager.getService("simphonebook"));
            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;
    }

Copy the code
  • You can see this in the code aboveaddIccRecordToEfMethods bybindercallsimphonebookThe service ofupdateAdnRecordsInEfBySearchForSubscriberMethod to add SIM information.

Simphonebook service implementation class is UiccPhoneBookController. Java

Simphonebook service

UiccPhoneBookController. Java code is as follows

public class UiccPhoneBookController extends IIccPhoneBook.Stub{...@Override
    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);
    }
       @Override
    public boolean
    updateAdnRecordsInEfBySearchForSubscriber(int subId, int efid, String oldTag,
            String oldPhoneNumber, String newTag, String newPhoneNumber,
            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; }}/** * get phone book interface manager object based on subscription. **/
    private IccPhoneBookInterfaceManager
            getIccPhoneBookInterfaceManager(int subId) {
        / / code 1
        int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
        try {
            / / code 2
            return mPhone[phoneId].getIccPhoneBookInterfaceManager();
        } catch (NullPointerException e) {
            Rlog.e(TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
            e.printStackTrace(); //To print stack trace
            return null;
        } catch (ArrayIndexOutOfBoundsException e) {
            Rlog.e(TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
            e.printStackTrace();
            return null; }}}Copy the code
  • Get the phoneId from subId at code 1.
  • After obtaining the phoneId in code 2, obtain it according to the phoneIdPhoneObject (mPhoneIs aPhoneArray), callgetIccPhoneBookInterfaceManagerMethods to getIccPhoneBookInterfaceManagerObject.
  • Code 3 CallIccPhoneBookInterfaceManager#updateAdnRecordsInEfBySearchMethod Update contact information stored in a SIM card.

/frameworks/opt/telephony/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java IccPhoneBookInterfaceManager. Java# updateAdnRecordsInEfBySearch code is as follows:

 public boolean
    updateAdnRecordsInEfBySearch (int efid,
            String oldTag, String oldPhoneNumber,
            String newTag, String newPhoneNumber, String pin2) {
        // Check permissions for code 1
        if(mPhone.getContext().checkCallingOrSelfPermission( android.Manifest.permission.WRITE_CONTACTS) ! = PackageManager.PERMISSION_GRANTED) {throw new SecurityException(
                    "Requires android.permission.WRITE_CONTACTS permission"); }... efid = updateEfForIccType(efid);// I'm not sure how it works
        synchronized(mLock) {
            checkThread();
            mSuccess = false;
            AtomicBoolean status = new AtomicBoolean(false);
            Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, status);
            AdnRecord oldAdn = new AdnRecord(oldTag, oldPhoneNumber);
            AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
            if(mAdnCache ! =null) {
                / / code 2
                mAdnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response);
                / / code 3
                waitForResult(status);
            } else {
                loge("Failure while trying to update by search due to uninitialised adncache"); }}return mSuccess;
    }
    private int updateEfForIccType(int efid) {
        // Check if we are trying to read ADN records
        if (efid == IccConstants.EF_ADN) {
            if (mPhone.getCurrentUiccAppType() == AppType.APPTYPE_USIM) {
                returnIccConstants.EF_PBR; }}return efid;
    }
    protected void waitForResult(AtomicBoolean status) {
        while(! status.get()) {try {
                mLock.wait();
            } catch (InterruptedException e) {
                logd("interrupted while trying to update by search"); }}}Copy the code
  • Called in code 2AdnRecordCache.java#updateAdnBySearchMethod to write the value
  • Called in code 3waitForResult()Method blocks the thread and waitsstatusIs set to true.
    private IccFileHandler mFh;// File operation Handler
    /**
     * Replace oldAdn with newAdn in ADN-like record in EF
     *
     * The ADN-like records must be read through requestLoadAllAdnLike() before
     *
     * @param efid must be one of EF_ADN, EF_FDN, and EF_SDN
     * @param oldAdn is the adn to be replaced
     *        If oldAdn.isEmpty() is ture, it insert the newAdn
     * @param newAdn is the adn to be stored
     *        If newAdn.isEmpty() is true, it delete the oldAdn
     * @param pin2 is required to update EF_FDN, otherwise must be null
     * @param response message to be posted when done
     *        response.exception hold the exception in error
     */
    public void updateAdnBySearch(int efid, AdnRecord oldAdn, AdnRecord newAdn,
            String pin2, Message response) {

        int extensionEF;
        extensionEF = extensionEfForEf(efid);

        if (extensionEF < 0) {
            sendErrorResponse(response, "EF is not known ADN-like EF:0x" +
                    Integer.toHexString(efid).toUpperCase());
            return;
        }

        ArrayList<AdnRecord>  oldAdnList;

        if (efid == EF_PBR) {
            oldAdnList = mUsimPhoneBookManager.loadEfFilesFromUsim();
        } else {
            oldAdnList = getRecordsIfLoaded(efid);
        }

        if (oldAdnList == null) {
            sendErrorResponse(response, "Adn list not exist for EF:0x" +
                    Integer.toHexString(efid).toUpperCase());
            return;
        }

        int index = -1;
        int count = 1;
        // code 1 iterates over the location of the contact information to be replaced ("" is passed when adding contacts)
        for (Iterator<AdnRecord> it = oldAdnList.iterator(); it.hasNext(); ) {
            if (oldAdn.isEqual(it.next())) {
                index = count;
                break;
            }
            count++;
        }

        if (index == -1) {
            sendErrorResponse(response, "Adn record don't exist for " + oldAdn);
            return;
        }

        if (efid == EF_PBR) {
            AdnRecord foundAdn = oldAdnList.get(index-1);
            efid = foundAdn.mEfid;
            extensionEF = foundAdn.mExtRecord;
            index = foundAdn.mRecordNumber;

            newAdn.mEfid = efid;
            newAdn.mExtRecord = extensionEF;
            newAdn.mRecordNumber = index;
        }

        Message pendingResponse = mUserWriteResponse.get(efid);

        if(pendingResponse ! =null) {
            sendErrorResponse(response, "Have pending update for EF:0x" +
                    Integer.toHexString(efid).toUpperCase());
            return;
        }

        mUserWriteResponse.put(efid, response);
        / / code 2
        new AdnRecordLoader(mFh).updateEF(newAdn, efid, extensionEF,
                index, pin2,
                obtainMessage(EVENT_UPDATE_ADN_DONE, efid, index, newAdn));
    }
    
    public ArrayList<AdnRecord> getRecordsIfLoaded(int efid) {
        return mAdnLikeFiles.get(efid);
    }
Copy the code
  • Code 1 iterates to query the location of the contact information to be replaced byIccProvider#addIccRecordToEfThe number and phone are both passed in"".
  • Called in code 2AdnRecordLoader.java#updateEFMethod to update contact information
    private IccFileHandler mFh;
    AdnRecordLoader(IccFileHandler fh) {
        // The telephony unit-test cases may create AdnRecords
        // in secondary threads
        super(Looper.getMainLooper());
        mFh = fh;
    }
    /** * Write adn to a EF SIM record * It will get the record size of EF record and compose hex adn array * then write the  hex array to EF record * *@param adn is set with alphaTag and phone number
     * @param ef EF fileid
     * @param extensionEF extension EF fileid
     * @param recordNumber 1-based record index
     * @param pin2 for CHV2 operations, must be null if pin2 is not needed
     * @param response will be sent to its handler when completed
     */
    public void
    updateEF(AdnRecord adn, int ef, int extensionEF, int recordNumber,
            String pin2, Message response) {
        mEf = ef;
        mExtensionEF = extensionEF;
        mRecordNumber = recordNumber;
        mUserResponse = response;
        mPin2 = pin2;
        / / code 1mFh.getEFLinearRecordSize( ef, getEFPath(ef), obtainMessage(EVENT_EF_LINEAR_RECORD_SIZE_DONE, adn)); }...Copy the code
  • Called in code 1IccFileHandler.java#javaMethod to obtain the file path of the corresponding SIM card based on the eFID value. The getEFPath method is an abstract method in ICCFileHandle. Java. For SIM, it is generally implemented asSIMFileHandler.java)

IccFileHandler.java#getEFLinearRecordSize

    protected final CommandsInterface mCi;
    /**
     * get record size for a linear fixed EF
     *
     * @param fileid EF id
     * @param path Path of the EF on the card
     * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[]
     *        int[0] is the record length int[1] is the total length of the EF
     *        file int[3] is the number of records in the EF file So int[0] *
     *        int[3] = int[1]
     */
    public void getEFLinearRecordSize(int fileid, String path, Message onLoaded) {
        String efPath = (path == null)? getEFPath(fileid) : path; Message response = obtainMessage(EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE,new LoadLinearFixedContext(fileid, efPath, onLoaded));
        / / code 1
        mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, efPath,
                    0.0, GET_RESPONSE_EF_SIZE_BYTES, null.null, mAid, response);
    }
Copy the code
  • Called in code 1iccIOForAppmethods

/frameworks/opt/telephony/src/java/com/android/internal/telephony/CommandsInterface.java

Its implementation class is/frameworks/opt/telephony/SRC/Java/com/android/internal/telephony/RIL. Java RIL. Java code

    static final String[] HIDL_SERVICE_NAME = {"slot1"."slot2"."slot3"};
@Override
    public void iccIOForApp(int command, int fileId, String path, int p1, int p2, int p3,
                 String data, String pin2, String aid, Message result) {
        / / code 1
        IRadio radioProxy = getRadioProxy(result);
        if(radioProxy ! =null) {
            RILRequest rr = obtainRequest(RIL_REQUEST_SIM_IO, result,
                    mRILDefaultWorkSource);

            if (RILJ_LOGD) {
                if (Build.IS_DEBUGGABLE) {
                    riljLog(rr.serialString() + "> iccIO: "
                            + requestToString(rr.mRequest) + " command = 0x"
                            + Integer.toHexString(command) + " fileId = 0x"
                            + Integer.toHexString(fileId) + " path = " + path + " p1 = "
                            + p1 + " p2 = " + p2 + " p3 = " + " data = " + data
                            + " aid = " + aid);
                } else {
                    riljLog(rr.serialString() + "> iccIO: " + requestToString(rr.mRequest));
                }
            }

            IccIo iccIo = new IccIo();
            iccIo.command = command;
            iccIo.fileId = fileId;
            iccIo.path = convertNullToEmptyString(path);
            iccIo.p1 = p1;
            iccIo.p2 = p2;
            iccIo.p3 = p3;
            iccIo.data = convertNullToEmptyString(data);
            iccIo.pin2 = convertNullToEmptyString(pin2);
            iccIo.aid = convertNullToEmptyString(aid);

            try {
                / / code 4
                radioProxy.iccIOForApp(rr.mSerial, iccIo);
            } catch (RemoteException | RuntimeException e) {
                handleRadioProxyExceptionForRR(rr, "iccIOForApp", e); }}}/** Returns a {@link IRadio} instance or null if the service is not available. */
    @VisibleForTesting
    public IRadio getRadioProxy(Message result) {
        // Code 2 checks whether there is a mobile network
        if(! mIsMobileNetworkSupported) {if (RILJ_LOGV) riljLog("getRadioProxy: Not calling getService(): wifi-only");
            if(result ! =null) {
                AsyncResult.forMessage(result, null,
                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
                result.sendToTarget();
            }
            return null;
        }

        if(mRadioProxy ! =null) {
            return mRadioProxy;
        }

        try {
            // Code 3 obtains the corresponding card slot based on the phoneId. The default slot is card slot 1
            mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId],
                    true);
            if(mRadioProxy ! =null) {
                mRadioProxy.linkToDeath(mRadioProxyDeathRecipient,
                        mRadioProxyCookie.incrementAndGet());
                mRadioProxy.setResponseFunctions(mRadioResponse, mRadioIndication);
            } else {
                riljLoge("getRadioProxy: mRadioProxy == null"); }}catch (RemoteException | RuntimeException e) {
            mRadioProxy = null;
            riljLoge("RadioProxy getService/setResponseFunctions: " + e);
        }

        if (mRadioProxy == null) {
            // getService() is a blocking call, so this should never happen
            riljLoge("getRadioProxy: mRadioProxy == null");
            if(result ! =null) {
                AsyncResult.forMessage(result, null, CommandException.fromRilErrno(RADIO_NOT_AVAILABLE)); result.sendToTarget(); }}return mRadioProxy;
    }
Copy the code
  • Code 1 gets the Radio service
  • Code 2 determines whether mobile networks are supported
  • Code 3 obtains the card slot based on the phoneId, and then obtains the service class of the corresponding card slot based on the card slot.IRadio.getServiceThe implementation class obtained is/hardware/ril/libril/ril_service.cpp?

.