class Log {
    static TAG = '[Phone]'
    static Debug = true
    static format(...msg) {
        let m = []
        for (let i = 0; i < msg.length; i++) {
            if (typeof msg[i] === 'object') {
                m.push(JSON.stringify(msg[i]))
            } else {
                m.push(msg[i])
            }
        }
        m = m.join(' ')
        return m
    }
    static i(...msg) {
        if (!this.Debug) return
        console.log(`\x1b[30m${this.TAG} ${this.format(...msg)}\x1b[0m`)
    }
    static w(...msg) {
        console.log(`\x1b[33m${this.TAG} ${this.format(...msg)}\x1b[0m`)
    }
    static e(...msg) {
        console.log(`\x1b[31m${this.TAG} ${this.format(...msg)}\x1b[0m`)
    }
    static s(...msg) {
        console.log(`\x1b[32m${this.TAG} ${this.format(...msg)}\x1b[0m`)
    }
}

function trace(tag) {
    Log.e((tag || '') + Java.use('android.util.Log').getStackTraceString(Java.use('java.lang.Throwable').$new()))
}

setImmediate(() => {
    Java.perform(function () {
        function checkPackage(name) {
            // return (
            //     name.startsWith('com.google.android.gsf') ||
            //     name.startsWith('com.google.android.gms') ||
            //     name.startsWith('com.google.android.apps') ||
            //     name.startsWith('com.example')
            // )
            return true
        }

        const SystemProperties = Java.use('android.os.SystemProperties')

        const PhoneInterfaceManager = Java.use('com.android.phone.PhoneInterfaceManager')
        PhoneInterfaceManager.getLine1NumberForDisplay.overload(
            'int',
            'java.lang.String',
            'java.lang.String'
        ).implementation = function (subId, callingPackage, callingFeatureId) {
            const res = this.getLine1NumberForDisplay(subId, callingPackage, callingFeatureId)
            if (!checkPackage(callingPackage)) {
                return res
            }
            Log.i(
                `spoof PhoneInterfaceManager.getLine1NumberForDisplay(${subId}, ${callingPackage}, ${callingFeatureId}): ${res} -> ${SystemProperties.get('persist.spoof.number')}`
            )
            return SystemProperties.get('persist.spoof.number')
        }

        PhoneInterfaceManager.getNetworkCountryIsoForPhone.overload('int').implementation = function (phoneId) {
            const res = this.getNetworkCountryIsoForPhone(phoneId)
            Log.i(`spoof PhoneInterfaceManager.getNetworkCountryIsoForPhone(${phoneId}): ${res} -> ${SystemProperties.get('persist.spoof.country')}`)
            return SystemProperties.get('persist.spoof.country')
        }

        PhoneInterfaceManager.getImeiForSlot.overload('int', 'java.lang.String', 'java.lang.String').implementation =
            function (slotId, callingPackage, callingFeatureId) {
                const res = this.getImeiForSlot(slotId, callingPackage, callingFeatureId)
                if (!checkPackage(callingPackage)) {
                    return res
                }
                Log.i(
                    `spoof PhoneInterfaceManager.getImeiForSlot(${slotId}, ${callingPackage}, ${callingFeatureId}): ${res} -> ${SystemProperties.get('persist.spoof.imei')}`
                )
                return SystemProperties.get('persist.spoof.imei')
            }

        SystemProperties.get.overload('java.lang.String').implementation = function (key) {
            if ('gsm.sim.operator.iso-country' === key) {
                Log.i(`spoof SystemProperties.get(${key}): ${SystemProperties.get('persist.spoof.country')}`)
                return SystemProperties.get('persist.spoof.country')
            }
            if ('gsm.sim.operator.numeric' === key) {
                Log.i(`spoof SystemProperties.get(${key}): ${SystemProperties.get('persist.spoof.mcc') + SystemProperties.get('persist.spoof.mnc')}`)
                return SystemProperties.get('persist.spoof.mcc') + SystemProperties.get('persist.spoof.mnc')
            }
            if ('gsm.operator.numeric' === key) {
                Log.i(`spoof SystemProperties.get(${key}): ${SystemProperties.get('persist.spoof.mcc') + SystemProperties.get('persist.spoof.mnc')}`)
                return SystemProperties.get('persist.spoof.mcc') + SystemProperties.get('persist.spoof.mnc')
            }
            return this.get(key)
        }

        const SubscriptionController = Java.use('com.android.internal.telephony.SubscriptionController')
        SubscriptionController.getSimStateForSlotIndex.overload('int').implementation = function (slotIndex) {
            const res = this.getSimStateForSlotIndex(slotIndex)
            Log.i(`spoof SubscriptionController.getSimStateForSlotIndex(${slotIndex}): ${res} -> 5`)
            return 5
        }
        SubscriptionController.getPhoneNumberFromFirstAvailableSource.overload(
            'int',
            'java.lang.String',
            'java.lang.String'
        ).implementation = function (subId, callingPackage, callingFeatureId) {
            const res = this.getPhoneNumberFromFirstAvailableSource(subId, callingPackage, callingFeatureId)
            if (!checkPackage(callingPackage)) {
                return res
            }
            Log.i(
                `spoof SubscriptionController.getPhoneNumberFromFirstAvailableSource(${subId}, ${callingPackage}, ${callingFeatureId}): ${res} -> ${SystemProperties.get('persist.spoof.number')}`
            )
            return SystemProperties.get('persist.spoof.number')
        }
        const SubscriptionInfo = Java.use('android.telephony.SubscriptionInfo')
        SubscriptionController.getActiveSubscriptionInfoList.overload('java.lang.String').implementation = function (
            callingPackage
        ) {
            const res = this.getActiveSubscriptionInfoList(callingPackage)
            if (!checkPackage(callingPackage)) {
                return res
            }
            Log.i(`spoof SubscriptionController.getActiveSubscriptionInfoList(${callingPackage})`)
            for (let i = 0; i < res.size(); i++) {
                const info = Java.cast(res.get(i), SubscriptionInfo)
                info.mMcc.value = SystemProperties.get('persist.spoof.mcc')
                info.mMnc.value = SystemProperties.get('persist.spoof.mnc')
                info.mCountryIso.value = SystemProperties.get('persist.spoof.country')
                info.mIccId.value = SystemProperties.get('persist.spoof.iccid')
            }
            SystemProperties.set('gsm.sim.operator.iso-country', SystemProperties.get('persist.spoof.country'))
            SystemProperties.set('gsm.sim.operator.numeric', SystemProperties.get('persist.spoof.mcc') + SystemProperties.get('persist.spoof.mnc'))
            SystemProperties.set('gsm.operator.numeric', SystemProperties.get('persist.spoof.mcc') + SystemProperties.get('persist.spoof.mnc'))
            return res
        }

        SubscriptionController.getActiveSubscriptionInfoList.overload(
            'java.lang.String',
            'java.lang.String'
        ).implementation = function (callingPackage, callingFeatureId) {
            const res = this.getActiveSubscriptionInfoList(callingPackage, callingFeatureId)
            if (!checkPackage(callingPackage)) {
                return res
            }
            Log.i(`spoof SubscriptionController.getActiveSubscriptionInfoList(${callingPackage}, ${callingFeatureId})`)
            for (let i = 0; i < res.size(); i++) {
                const info = Java.cast(res.get(i), SubscriptionInfo)
                info.mMcc.value = SystemProperties.get('persist.spoof.mcc')
                info.mMnc.value = SystemProperties.get('persist.spoof.mnc')
                info.mCountryIso.value = SystemProperties.get('persist.spoof.country')
                info.mIccId.value = SystemProperties.get('persist.spoof.iccid')
            }
            SystemProperties.set('gsm.sim.operator.iso-country', SystemProperties.get('persist.spoof.country'))
            SystemProperties.set('gsm.sim.operator.numeric', SystemProperties.get('persist.spoof.mcc') + SystemProperties.get('persist.spoof.mnc'))
            SystemProperties.set('gsm.operator.numeric', SystemProperties.get('persist.spoof.mcc') + SystemProperties.get('persist.spoof.mnc'))
            return res
        }

        SubscriptionController.getActiveSubscriptionInfoList.overload('java.lang.String').implementation = function (
            callingPackage
        ) {
            const res = this.getActiveSubscriptionInfoList(callingPackage)
            if (!checkPackage(callingPackage)) {
                return res
            }
            Log.i(`spoof SubscriptionController.getActiveSubscriptionInfoList(${callingPackage})`)
            for (let i = 0; i < res.size(); i++) {
                const info = Java.cast(res.get(i), SubscriptionInfo)
                info.mMcc.value = SystemProperties.get('persist.spoof.mcc')
                info.mMnc.value = SystemProperties.get('persist.spoof.mnc')
                info.mCountryIso.value = SystemProperties.get('persist.spoof.country')
                info.mIccId.value = SystemProperties.get('persist.spoof.iccid')
            }
            SystemProperties.set('gsm.sim.operator.iso-country', SystemProperties.get('persist.spoof.country'))
            SystemProperties.set('gsm.sim.operator.numeric', SystemProperties.get('persist.spoof.mcc') + SystemProperties.get('persist.spoof.mnc'))
            SystemProperties.set('gsm.operator.numeric', SystemProperties.get('persist.spoof.mcc') + SystemProperties.get('persist.spoof.mnc'))
            return res
        }

        const PhoneSubInfoController = Java.use('com.android.internal.telephony.PhoneSubInfoController')
        PhoneSubInfoController.getIccSerialNumberForSubscriber.overload(
            'int',
            'java.lang.String',
            'java.lang.String'
        ).implementation = function (subId, callingPackage, callingFeatureId) {
            const res = this.getIccSerialNumberForSubscriber(subId, callingPackage, callingFeatureId)
            if (!checkPackage(callingPackage)) {
                return res
            }
            Log.i(
                `spoof PhoneInterfaceManager.getIccSerialNumberForSubscriber(${subId}, ${callingPackage}, ${callingFeatureId}): ${res} -> ${SystemProperties.get('persist.spoof.iccid')}`
            )
            return SystemProperties.get('persist.spoof.iccid')
        }
        PhoneSubInfoController.getSubscriberIdForSubscriber.overload(
            'int',
            'java.lang.String',
            'java.lang.String'
        ).implementation = function (subId, callingPackage, callingFeatureId) {
            const res = this.getSubscriberIdForSubscriber(subId, callingPackage, callingFeatureId)
            if (!checkPackage(callingPackage)) {
                return res
            }
            Log.i(
                `spoof PhoneInterfaceManager.getSubscriberIdForSubscriber(${subId}, ${callingPackage}, ${callingFeatureId}): ${res} -> ${SystemProperties.get('persist.spoof.imsi')}`
            )
            return SystemProperties.get('persist.spoof.imsi')
        }
    })
})
