ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • CameraX api 5-2편 : OCR + 여권(MRZ + NFC)
    회사 생활/여권 NFC (CameraX + OCR + NFC) 2023. 11. 25. 15:30

    4편에서 OCR과 5편에서 MRZ부분을 다뤘기 때문에 NFC부분만 다루겠다.

     

    우선 먼저 안드로이드에서 여권 NFC를 하기위해 필요한 라이브러리 부터 불러온다.

     

    build.gradle(app)

    implementation 'org.jmrtd:jmrtd:0.7.18'
    implementation 'net.sf.scuba:scuba-sc-android:0.0.18'
    implementation 'com.madgag.spongycastle:prov:1.58.0.0'
    implementation 'edu.ucar:jj2000:5.2'
    implementation 'com.github.mhshams:jnbis:1.1.0'

     

    그리고 NFC를 하기위해 필요한 여권번호, 생년월인, 여권만료일자를 가져온다. 

    val intent: Intent = getIntent()
    mrzInfo = (intent.getSerializableExtra(MRZ_RESULT) as MRZInfo?)!!

     

    OCR을 통해 CameraX를 이용해 여권 MRZ 코드로 부터 가져온 데이터를 MRZInfo에 넣어주고 MRZInfo를 Intent로 다른 Activity에 넘겨 주었다.

     

    MRZInfo class build.gradle에 'implementation 'org.jmrtd:jmrtd:0.7.18' 가 추가 되었을때 같이 추가된다. Serializable를 상속해서 Intent로 넘겨줄수 있게 되어있다. (대충 아래와 같은 필드를 갖고 있다.)

    더보기
      private int documentType;
    
      private String documentCode;
      private String issuingState;
      private String primaryIdentifier;
      private String secondaryIdentifier;
      private String nationality;
      private String documentNumber;
      private String dateOfBirth;
      private Gender gender;
      private String dateOfExpiry;
      private char documentNumberCheckDigit;
      private char dateOfBirthCheckDigit;
      private char dateOfExpiryCheckDigit;
      private char compositeCheckDigit;
      private String optionalData1; /* NOTE: holds personal number for some issuing states (e.g. NL), but is used to hold (part of) document number for others. */
      private String optionalData2;

     

    그 다음 NFC를 사용하도록 설정해 준다.  NFC는 application class에 설정해 줬다.  

    class MainApplication : Application() {
        private lateinit var adapter: NfcAdapter
    
    	... 생략 ...
        
        override fun onCreate() {
            super.onCreate()
            adapter = NfcAdapter.getDefaultAdapter(this)
        }
        
        ... 생략 ...
    
    
        fun getNFCAdapter(): NfcAdapter {
            return adapter
        }
    
        //NFC 진동이 작동되는거 자체를 막음
        fun stopNFCReader(mActivity: Activity) {
            if (adapter != null)
                adapter.enableReaderMode(mActivity, null, NfcAdapter.STATE_TURNING_OFF, null);
        }
    }

     

    NFC를 사용하기 위해서는 NFCAdapter를 불러와야한다. 

     

    NFCAdapter를 NFC에 사용되는 곳이 아닌 Application class에 만들어준 이유는 NFC를 활성화 시키고 나서 NFC reading이 되는 것을 막고 싶어도 안막아졌다. 

    (이부분은 아래에서 NFC를 활성화 시키면서 한번 더 언급하겠다. 참고1, 참고2)

     

    그래서 각 Activity 별로 저기 있는 stopNFCReader()를 사용해서 정지시켜주었고, 모든 Activity마다 추가 시킬 경우 코드의 중복이 많아져서 저렇게 했다.

     

    실제 NFC를 진행하는 Activity를 제외한 모든 Activity에 아래와 같이 stopNFCReader()를 불러와 NFC가 읽혀지는 것을 방지했다. (그렇게 하지 않으면 여권과 가까이 있을 경우 진동이 느껴진 후 NFC를 사용하는 다른 앱이 켜졌다.)

    override fun onStart() {
        super.onStart()
        (application as MainApplication).stopNFCReader(this)
    }
    

     

     

    이제 NFC를 활성화 시켜주는 부분이다. 이부분은 NFC를 활용하는 Activity에서 해주면 된다.

       override fun onResume() {
            super.onResume()
            Log.d("123123123", "NfcScanActivity - onResume")
    
            if (adapter == null) {
                // 기기가 NFC를 지원하지 않음.
                Toast.makeText(this, "NFC 기능이 없어서 사용할 수가 없습니다..", Toast.LENGTH_SHORT).show()
            } else if (!adapter.isEnabled) {
                // NFC가 비활성화되어 있음
                val intent = Intent(android.provider.Settings.ACTION_NFC_SETTINGS)
                startActivity(intent)
            } else {
                // NFC가 켜져있음
                val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE)
                val filter: Array<Array<String>> = arrayOf(arrayOf("android.nfc.tech.IsoDep"))
                adapter.enableForegroundDispatch(this, pendingIntent, null, filter)
            }
        }
    override fun onPause() {
        super.onPause()
        if (adapter != null)
            adapter.disableForegroundDispatch(this)
    }

     

    스마트폰 기종에 따라서 NFC칩이 없는 경우도 있고, NFC가 꺼져있는 경우도 있다. 그래서 그 부분을 나눠서 구현했다.

    adapter.enableForegroundDispatch(this, pendingIntent, null, filter

     

    enableForegroundDispatch를 사용해서 NFC를 활성화 시켜준다. 

     

    PendingIntent는 목적은 다른 애플리케이션의 권한을 허용하여 가지고 있는 Intent를 본인 앱에서 실행할 수 있게 해주는 것이다.

     

    filter를 이용해서 여러 NFC 이벤트중 "android.nfc.tech.IsoDep" 의 종류만 처리한다. 

    더보기
        <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
            <tech-list>
                <tech>android.nfc.tech.IsoDep</tech>
                <tech>android.nfc.tech.NfcA</tech>
                <tech>android.nfc.tech.NfcB</tech>
                <tech>android.nfc.tech.NfcF</tech>
                <tech>android.nfc.tech.NfcV</tech>
                <tech>android.nfc.tech.Ndef</tech>
                <tech>android.nfc.tech.NdefFormatable</tech>
                <tech>android.nfc.tech.MifareClassic</tech>
                <tech>android.nfc.tech.MifareUltralight</tech>
            </tech-list>
        </resources>

     

    공부해보면 재미있을 것 같기는 한데 시간이 없어서 생략......

     

    가장 의문인 부분은 disableForegroundDispatch 부분이다. 이것을 onPause에 추가시킨다고 해도 NFC가 비활성화가 되는 것이 아니었다!!!

     

    그래서 앞서 얘기했듯이 Application class에 정의해둔 stopNFCReader를 활용해서 다른 Activity에서는 NFC가 안되게 막아두었다.

     

     

    스마트폰이 NFC관련 데이터가 넘어올 경우 onNewIntent로 넘어온다.  

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
    
        if (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action) {
            val tag: Tag? = intent.extras?.getParcelable(NfcAdapter.EXTRA_TAG)
            if (tag != null) {
                if (listOf(*tag.techList).contains("android.nfc.tech.IsoDep") && application.nfcIsRunning) {
                    if (((passportNumber != "") && !passportNumber.isEmpty()
                                && (expirationDate != "") && !expirationDate.isEmpty()
                                && (birthDate != "") && !birthDate.isEmpty())
                    ) {
                        val bacKey: BACKeySpec = BACKey(passportNumber, birthDate, expirationDate)
                        ReadTask(IsoDep.get(tag), bacKey, this, adapter, this).execute()
    
                    } else {
                        Log.d("onPostExecute", "onPostExecute444")
                    }
                } else {
                    Log.d("onPostExecute", "onPostExecute333")
                }
            }
        } else {
            Log.d("onPostExecute", "onPostExecute222")
        }
    }

     

    여권번호, 만료일자, 생일 정보가 비어있지 않다면 background에서 NFC로부터 정보를 읽어오는 코드가 실행된다.

     

     

    다른 예제들을 보니 AsyncTask로 구현되어 있었다. 하지만 AsyncTask는 deprecated 되서 그것을 대체하는 것까지 찾아서 구현해주었다. (이부분은 좀더 공부할 필요성이 있을 것 같다.)

     

    NFC한 데이터를 불러오는 코드이다

    더보기
        class ReadTask constructor(
            private val isoDep: IsoDep,
            private val bacKey: BACKeySpec,
            private val mContext: Context,
        ) : ThreadTask<Void?, Void?, Exception?>() {
            private var eDocument: EDocument = EDocument()
            private var personDetails: PersonDetails = PersonDetails()
            private var additionalPersonDetails: AdditionalPersonDetails = AdditionalPersonDetails()
            private var additionalDocumentDetails: AdditionalDocumentDetails =
                AdditionalDocumentDetails()
    
            private val dateUtil = DateUtil()
            private val imageUtil = ImageUtil()
    
            override fun onPreExecute() {
                progressBar.visibility = View.VISIBLE
            }
    
            override fun doInBackground(vararg: Void?): Exception? {
                try {
                    val cardService: CardService = CardService.getInstance(isoDep)
                    cardService.open()
                    val service = PassportService(
                        cardService, PassportService.NORMAL_MAX_TRANCEIVE_LENGTH,
                        PassportService.DEFAULT_MAX_BLOCKSIZE, true, false
                    )
                    service.open()
                    var paceSucceeded = false
                    try {
                        val cardSecurityFile =
                            CardSecurityFile(service.getInputStream(PassportService.EF_CARD_SECURITY))
                        val securityInfoCollection: Collection<SecurityInfo> =
                            cardSecurityFile.securityInfos
    
                        for (securityInfo: SecurityInfo? in securityInfoCollection) {
                            if (securityInfo is PACEInfo) {
                                val paceInfo: PACEInfo = securityInfo
                                service.doPACE(
                                    bacKey,
                                    paceInfo.objectIdentifier,
                                    PACEInfo.toParameterSpec(paceInfo.parameterId),
                                    null
                                )
                                paceSucceeded = true
                            }
                        }
                    } catch (e: Exception) {
                        Log.d("goodgood", "goodgood444")
    
                        Log.w("TAG", e)
                    }
                    service.sendSelectApplet(paceSucceeded)
                    if (!paceSucceeded) {
                        try {
                            service.getInputStream(PassportService.EF_COM).read()
                        } catch (e: java.lang.Exception) {
                            service.doBAC(bacKey)
                        }
                    }
    
                    // -- Personal Details -- //
                    val dg1In: CardFileInputStream =
                        service.getInputStream(PassportService.EF_DG1)
                    val dg1File = DG1File(dg1In)
                    val mrzInfo: MRZInfo = dg1File.mrzInfo
                    personDetails.documentType = mrzInfo.documentType.toString()
                    personDetails.name =
                        mrzInfo.secondaryIdentifier.replace("<", " ").trim { it <= ' ' }
                    personDetails.surname =
                        mrzInfo.primaryIdentifier.replace("<", " ").trim { it <= ' ' }
    
                    personDetails.personalNumber = mrzInfo.personalNumber
                    personDetails.gender = mrzInfo.gender.toString()
                    personDetails.birthDate = dateUtil.convertFromMrzDate(mrzInfo.dateOfBirth)
                    personDetails.expiryDate = dateUtil.convertFromMrzDate(mrzInfo.dateOfExpiry)
                    personDetails.serialNumber = mrzInfo.documentNumber
                    personDetails.nationality = mrzInfo.nationality
                    personDetails.issuerAuthority = mrzInfo.issuingState
    
                    Log.d("goodgood", personDetails.name!!)
    
                    // -- Face Image -- //
                    val dg2In: CardFileInputStream =
                        service.getInputStream(PassportService.EF_DG2)
                    val dg2File = DG2File(dg2In)
                    val faceInfos: List<FaceInfo> = dg2File.faceInfos
                    val allFaceImageInfos: MutableList<FaceImageInfo> = ArrayList()
                    for (faceInfo: FaceInfo in faceInfos) {
                        allFaceImageInfos.addAll(faceInfo.faceImageInfos)
                    }
                    if (allFaceImageInfos.isNotEmpty()) {
                        val faceImageInfo: FaceImageInfo = allFaceImageInfos.iterator().next()
                        val image: Image = imageUtil.getImage(mContext, faceImageInfo)
                        personDetails.faceImage = image.bitmapImage
                        personDetails.faceImageBase64 = image.base64Image
                    }
    
                    // -- Fingerprint (if exist)-- //
                    try {
                        val dg3In: CardFileInputStream =
                            service.getInputStream(PassportService.EF_DG3)
                        val dg3File = DG3File(dg3In)
                        val fingerInfos = dg3File.fingerInfos
                        val allFingerImageInfos: MutableList<FingerImageInfo> = ArrayList()
                        for (fingerInfo in fingerInfos) {
                            allFingerImageInfos.addAll(fingerInfo.fingerImageInfos)
                        }
                        val fingerprintsImage: MutableList<Bitmap> = ArrayList()
                        if (allFingerImageInfos.isNotEmpty()) {
                            for (fingerImageInfo in allFingerImageInfos) {
                                val image: Image = imageUtil.getImage(mContext, fingerImageInfo)
                                fingerprintsImage.add(image.bitmapImage)
                            }
                            personDetails.fingerprints = fingerprintsImage
                        }
                    } catch (e: java.lang.Exception) {
                        Log.w("TAG", e)
                    }
    
                    // -- Portrait Picture -- //
                    try {
                        val dg5In: CardFileInputStream =
                            service.getInputStream(PassportService.EF_DG5)
                        val dg5File = DG5File(dg5In)
                        val displayedImageInfos = dg5File.images
                        if (displayedImageInfos.isNotEmpty()) {
                            val displayedImageInfo: DisplayedImageInfo =
                                displayedImageInfos.iterator().next()
                            val image = imageUtil.getImage(mContext, displayedImageInfo)
                            personDetails.portraitImage = image.bitmapImage
                            personDetails.portraitImageBase64 = image.base64Image
                        }
                    } catch (e: java.lang.Exception) {
                        Log.w("TAG", e)
                    }
    
    //                // -- Signature (if exist) -- //
                    try {
                        val dg7In: CardFileInputStream =
                            service.getInputStream(PassportService.EF_DG7)
                        val dg7File = DG7File(dg7In)
                        val signatureImageInfos = dg7File.images
                        if (signatureImageInfos.isNotEmpty()) {
                            val displayedImageInfo: DisplayedImageInfo =
                                signatureImageInfos.iterator().next()
                            val image = imageUtil.getImage(mContext, displayedImageInfo)
                            personDetails.portraitImage = image.bitmapImage
                            personDetails.portraitImageBase64 = image.base64Image
                        }
                    } catch (e: java.lang.Exception) {
                        Log.w("TAG", e)
                    }
    
                    // -- Additional Details (if exist) -- //
                    try {
                        val dg11In = service.getInputStream(PassportService.EF_DG11)
                        val dg11File = DG11File(dg11In)
                        if (dg11File.length > 0) {
    
                            additionalPersonDetails.nameOfHolder = dg11File.nameOfHolder
                            additionalPersonDetails.otherNames = dg11File.otherNames
                            additionalPersonDetails.personalNumber = dg11File.personalNumber
                            additionalPersonDetails.placeOfBirth = dg11File.placeOfBirth
                            additionalPersonDetails.permanentAddress = dg11File.permanentAddress
                            additionalPersonDetails.telephone = dg11File.telephone
                            additionalPersonDetails.profession = dg11File.profession
                            additionalPersonDetails.title = dg11File.title
                            additionalPersonDetails.personalSummary = dg11File.personalSummary
                            additionalPersonDetails.proofOfCitizenship =
                                dg11File.proofOfCitizenship
                            additionalPersonDetails.otherValidTDNumbers =
                                dg11File.otherValidTDNumbers
                            additionalPersonDetails.custodyInformation =
                                dg11File.custodyInformation
                        }
                    } catch (e: java.lang.Exception) {
                        Log.w("TAG", e)
                    }
    
                    try {
                        val dg12In = service.getInputStream(PassportService.EF_DG12)
                        val dg12File = DG12File(dg12In)
    
                        additionalDocumentDetails.issuingAuthority = dg12File.issuingAuthority
                        additionalDocumentDetails.dateOfIssue = dg12File.dateOfIssue
                        additionalDocumentDetails.namesOfOtherPersons = dg12File.namesOfOtherPersons
                        additionalDocumentDetails.endorsementsAndObservations =
                            dg12File.endorsementsAndObservations
                        additionalDocumentDetails.taxOrExitRequirements = dg12File.taxOrExitRequirements
                        additionalDocumentDetails.imageOfFront = dg12File.imageOfFront
                        additionalDocumentDetails.imageOfRear = dg12File.imageOfRear
    
                    } catch (e: java.lang.Exception) {
    
                    }
    
                    eDocument.setAdditionalDocumentDetails(additionalDocumentDetails)
                    eDocument.setPersonDetails(personDetails)
                    eDocument.setAdditionalPersonDetails(additionalPersonDetails)
                } catch (e: java.lang.Exception) {
                    return e
                }
                return null
            }
    
            override fun onPostExecute(exception: Exception?) {
                progressBar.visibility = View.INVISIBLE
                if (exception == null) {
                    val intent = Intent(mContext, ResultActivity::class.java)
                    intent.putExtra("EDOCUMENT", eDocument)
                    mContext.startActivity(intent)
                } else {
                    Log.d("onPostExecute", exception.toString())
                }
            }
        }
    
        abstract class ThreadTask<T1, T2, T3> : Runnable {
    
            // Argument
            private var mArgument: T2? = null
    
            // Result
            private var mResult: T3? = null
    
            // Handle the result
            private val WORK_DONE = 0
    
            // Execute
            fun execute() {
                onPreExecute()
                // Begin thread work
                val thread = Thread(this)
                thread.start()
            }
    
            override fun run() {
                // Call doInBackground
                mResult = doInBackground(mArgument)
    
                // Notify main thread that the work is done
                mResultHandler.sendEmptyMessage(0)
            }
    
            private var mResultHandler: Handler = object : Handler(Looper.getMainLooper()) {
                override fun handleMessage(msg: Message) {
                    super.handleMessage(msg)
    
                    // Call onPostExecute
                    onPostExecute(mResult)
                }
            }
    
    
            // onPreExecute
            protected abstract fun onPreExecute()
    
            // doInBackground
            protected abstract fun doInBackground(vararg: T2?): T3?
    
            // onPostExecute
            protected abstract fun onPostExecute(result: T3?)
        }

     

    AsyncTask로 NFC한 데이터를 불러오는 코드도 혹시 몰라 남겨둔다.

    더보기
        class ReadTask constructor(
            private val isoDep: IsoDep,
            private val bacKey: BACKeySpec,
            val mContext: Context,
        ) : AsyncTask<Void?, Void?, Exception?>() {
            var eDocument: EDocument = EDocument()
            var personDetails: PersonDetails = PersonDetails()
            var additionalPersonDetails: AdditionalPersonDetails = AdditionalPersonDetails()
            val dateUtil = DateUtil()
            val imageUtil = ImageUtil()
            override fun doInBackground(vararg params: Void?): Exception? {
                try {
                    val cardService: CardService = CardService.getInstance(isoDep)
                    cardService.open()
                    val service = PassportService(
                        cardService, PassportService.NORMAL_MAX_TRANCEIVE_LENGTH,
                        PassportService.DEFAULT_MAX_BLOCKSIZE, true, false
                    )
                    service.open()
                    var paceSucceeded = false
                    try {
                        val cardSecurityFile =
                            CardSecurityFile(service.getInputStream(PassportService.EF_CARD_SECURITY))
                        val securityInfoCollection: Collection<SecurityInfo> =
                            cardSecurityFile.securityInfos
    
                        for (securityInfo: SecurityInfo? in securityInfoCollection) {
                            if (securityInfo is PACEInfo) {
                                val paceInfo: PACEInfo = securityInfo
                                service.doPACE(
                                    bacKey,
                                    paceInfo.objectIdentifier,
                                    PACEInfo.toParameterSpec(paceInfo.parameterId),
                                    null
                                )
                                paceSucceeded = true
                            }
                        }
                    } catch (e: Exception) {
                        Log.w("TAG", e)
                    }
                    service.sendSelectApplet(paceSucceeded)
                    if (!paceSucceeded) {
                        try {
                            service.getInputStream(PassportService.EF_COM).read()
                        } catch (e: java.lang.Exception) {
                            service.doBAC(bacKey)
                        }
                    }
    
                    // -- Personal Details -- //
                    val dg1In: CardFileInputStream = service.getInputStream(PassportService.EF_DG1)
                    val dg1File: DG1File = DG1File(dg1In)
                    val mrzInfo: MRZInfo = dg1File.mrzInfo
                    personDetails.name =
                        mrzInfo.secondaryIdentifier.replace("<", " ").trim { it <= ' ' }
                    personDetails.surname =
                        mrzInfo.primaryIdentifier.replace("<", " ").trim { it <= ' ' }
    
                    personDetails.personalNumber = mrzInfo.personalNumber
                    personDetails.gender = mrzInfo.gender.toString()
                    personDetails.birthDate = dateUtil.convertFromMrzDate(mrzInfo.dateOfBirth)
                    personDetails.expiryDate = dateUtil.convertFromMrzDate(mrzInfo.dateOfExpiry)
                    personDetails.serialNumber = mrzInfo.documentNumber
                    personDetails.nationality = mrzInfo.nationality
                    personDetails.issuerAuthority = mrzInfo.issuingState
    
                    Log.d(
                        "MRZInfoMRZInfo",
                        "name : " + mrzInfo.secondaryIdentifier.replace("<", " ").trim { it <= ' ' })
    
    
                    // -- Face Image -- //
                    val dg2In: CardFileInputStream = service.getInputStream(PassportService.EF_DG2)
                    val dg2File = DG2File(dg2In)
                    val faceInfos: List<FaceInfo> = dg2File.faceInfos
                    val allFaceImageInfos: MutableList<FaceImageInfo> = ArrayList()
                    for (faceInfo: FaceInfo in faceInfos) {
                        allFaceImageInfos.addAll(faceInfo.faceImageInfos)
                    }
                    if (allFaceImageInfos.isNotEmpty()) {
                        val faceImageInfo: FaceImageInfo = allFaceImageInfos.iterator().next()
                        val image: Image = imageUtil.getImage(mContext, faceImageInfo)
                        personDetails.faceImage = image.bitmapImage
                        personDetails.faceImageBase64 = image.base64Image
                    }
    
                    // -- Fingerprint (if exist)-- //
                    try {
                        val dg3In: CardFileInputStream = service.getInputStream(PassportService.EF_DG3)
                        val dg3File = DG3File(dg3In)
                        val fingerInfos = dg3File.fingerInfos
                        val allFingerImageInfos: MutableList<FingerImageInfo> = ArrayList()
                        for (fingerInfo in fingerInfos) {
                            allFingerImageInfos.addAll(fingerInfo.fingerImageInfos)
                        }
                        val fingerprintsImage: MutableList<Bitmap> = ArrayList()
                        if (allFingerImageInfos.isNotEmpty()) {
                            for (fingerImageInfo in allFingerImageInfos) {
                                val image: Image = imageUtil.getImage(mContext, fingerImageInfo)
                                fingerprintsImage.add(image.bitmapImage)
                            }
                            personDetails.fingerprints = fingerprintsImage
                        }
                    } catch (e: java.lang.Exception) {
                        Log.w("TAG", e)
                    }
    
                    // -- Portrait Picture -- //
                    try {
                        val dg5In: CardFileInputStream = service.getInputStream(PassportService.EF_DG5)
                        val dg5File = DG5File(dg5In)
                        val displayedImageInfos = dg5File.images
                        if (displayedImageInfos.isNotEmpty()) {
                            val displayedImageInfo: DisplayedImageInfo =
                                displayedImageInfos.iterator().next()
                            val image = imageUtil.getImage(mContext, displayedImageInfo)
                            personDetails.portraitImage = image.bitmapImage
                            personDetails.portraitImageBase64 = image.base64Image
                        }
                    } catch (e: java.lang.Exception) {
                        Log.w("TAG", e)
                    }
    
    //                // -- Signature (if exist) -- //
                    try {
                        val dg7In: CardFileInputStream = service.getInputStream(PassportService.EF_DG7)
                        val dg7File = DG7File(dg7In)
                        val signatureImageInfos = dg7File.images
                        if (signatureImageInfos.isNotEmpty()) {
                            val displayedImageInfo: DisplayedImageInfo =
                                signatureImageInfos.iterator().next()
                            val image = imageUtil.getImage(mContext, displayedImageInfo)
                            personDetails.portraitImage = image.bitmapImage
                            personDetails.portraitImageBase64 = image.base64Image
                        }
                    } catch (e: java.lang.Exception) {
                        Log.w("TAG", e)
                    }
    
                    // -- Additional Details (if exist) -- //
                    try {
                        val dg11In = service.getInputStream(PassportService.EF_DG11)
                        val dg11File = DG11File(dg11In)
                        if (dg11File.length > 0) {
                            additionalPersonDetails.custodyInformation = dg11File.custodyInformation
                            additionalPersonDetails.nameOfHolder = dg11File.nameOfHolder
                            additionalPersonDetails.fullDateOfBirth = dg11File.fullDateOfBirth
                            additionalPersonDetails.otherNames = dg11File.otherNames
                            additionalPersonDetails.otherValidTDNumbers = dg11File.otherValidTDNumbers
                            additionalPersonDetails.permanentAddress = dg11File.permanentAddress
                            additionalPersonDetails.personalNumber = dg11File.personalNumber
                            additionalPersonDetails.personalSummary = dg11File.personalSummary
                            additionalPersonDetails.placeOfBirth = dg11File.placeOfBirth
                            additionalPersonDetails.profession = dg11File.profession
                            additionalPersonDetails.proofOfCitizenship = dg11File.proofOfCitizenship
                            additionalPersonDetails.tag = dg11File.tag
                            additionalPersonDetails.tagPresenceList = dg11File.tagPresenceList
                            additionalPersonDetails.telephone = dg11File.telephone
                            additionalPersonDetails.title = dg11File.title
                        }
                    } catch (e: java.lang.Exception) {
                        Log.w("TAG", e)
                    }
    
                    eDocument.setPersonDetails(personDetails)
                    eDocument.setAdditionalPersonDetails(additionalPersonDetails)
                } catch (e: java.lang.Exception) {
                    return e
                }
                return null
            }
    
            override fun onPostExecute(exception: Exception?) {
                Log.d("onPostExecute", "onPostExecute555")
                if (exception == null) {
                    val intent = Intent(mContext, ResultActivity::class.java)
                    intent.putExtra("EDOCUMENT", eDocument)
                    mContext.startActivity(intent)
                } else {
                    Log.d("onPostExecute", exception.toString())
                }
            }
        }

     

    아래는 NFC로 읽어올수 있는 데이터의 정보이다

    출처 : https://developers.innovatrics.com/digital-onboarding/docs/functionalities/document/nfc-reading/

     

    DG1부터 DG16까지 여러가지 정보를 불러올수 있다. 다만 NFC칩에 들어있는 정보중 어떤건 필수로 들어있고 어떤건 선택적으로 들어있어서 신여권 구여권 각 나라별로 들어있는 정보가 다르다.

     

    github 주소 : 

    https://github.com/tvroom88/AIO_Android_Kotlin_Support_Material/tree/main/CameraX/CameraX_5_OCR_NFC

Designed by Tistory.