一般安卓fastboot刷机需要unlock device,也叫unlock bootloader,我想多半和lock/unlock在bootloader中实现有关,ok,下面就来看下相关代码,参考高通平台P LK。

device_info中记录了是否解锁,有两个:is_unlocked, is_unlock_critical:

static device_info device = {DEVICE_MAGIC,0,0,0,0,{0},{0},{0},1,{0},0,{0}};
struct device_info
{
    unsigned char magic[DEVICE_MAGIC_SIZE];
    bool is_unlocked; //tj: here
    bool is_tampered;
    bool is_unlock_critical; //tj: here
    bool charger_screen_enabled;
    char display_panel[MAX_PANEL_ID_LEN];
    char bootloader_version[MAX_VERSION_LEN];
    char radio_version[MAX_VERSION_LEN];
    bool verity_mode; // 1 = enforcing, 0 = logging
    ...
};
enum unlock_type {
    UNLOCK = 0,
    UNLOCK_CRITICAL,
};

刷机代码有提到区别:

ifeq ($(BOARD_AVB_ENABLE),true)
  VERIFIED_BOOT_2 := VERIFIED_BOOT_2=1
else
  VERIFIED_BOOT_2 := VERIFIED_BOOT_2=0
endif

BOARD_AVB_ENABLE就是Android Verified Boot, 官方介绍: https://source.android.google.cn/security/verifiedboot/avb

void cmd_flash_mmc(const char *arg, void *data, unsigned sz)
{
...
#if VERIFIED_BOOT || VERIFIED_BOOT_2
    if (target_build_variant_user())
    {
        /* if device is locked:
         * common partition will not allow to be flashed
         * critical partition will allow to flash image.
         */
        if(!device.is_unlocked && !critical_flash_allowed(arg)) {
            fastboot_fail("Partition flashing is not allowed");
            return;
        }

        /* if device critical is locked:
         * common partition will allow to be flashed
         * critical partition will not allow to flash image.
         */
        if (VB_M <= target_get_vb_version() &&
            !device.is_unlock_critical &&
            critical_flash_allowed(arg)) {
                fastboot_fail("Critical partition flashing is not allowed");
                return;
        }
    }
#endif

看下critical_flash_allowed:

static const char *critical_flash_allowed_ptn[] = {
    "aboot",
    "rpm",
    "tz",
    "sbl",
    "sdi",
    "sbl1",
    "xbl",
    "hyp",
    "pmic",
    "bootloader",
    "devinfo",
    "partition"};

static bool critical_flash_allowed(const char * entry)
{
    uint32_t i = 0;
    if (entry == NULL)
        return false;

    for (i = 0; i < ARRAY_SIZE(critical_flash_allowed_ptn); i++) {
        if(!strcmp(entry, critical_flash_allowed_ptn[i]))
            return true;
    }
    return false;
}

userdebug刷机不care这个unlock。

read_device_info会对这两个lock赋值,前提是只要分区里magic不是DEVICE_MAGIC:

        if (memcmp(info->magic, DEVICE_MAGIC, DEVICE_MAGIC_SIZE))
        {
            memcpy(info->magic, DEVICE_MAGIC, DEVICE_MAGIC_SIZE);
            if (is_secure_boot_enable()) {
                info->is_unlocked = 0;
#if VERIFIED_BOOT || VERIFIED_BOOT_2
                if (VB_M <= target_get_vb_version())
                    info->is_unlock_critical = 0;
#endif
            } else {
                info->is_unlocked = 1;
#if VERIFIED_BOOT || VERIFIED_BOOT_2
                if (VB_M <= target_get_vb_version())
                    info->is_unlock_critical = 1;
#endif
            }
            info->is_tampered = 0;
            info->charger_screen_enabled = 0;
#if VERIFIED_BOOT || VERIFIED_BOOT_2
            if (VB_M <= target_get_vb_version())
                info->verity_mode = 1; //enforcing by default
#endif
            write_device_info(info); //tj: 写入
        }

当产品release后一般都是secure boot,可见都是lock的,这个标记写到哪里了?看write_device_info:

//tj: for emmc 
#if USE_RPMB_FOR_DEVINFO
        if (VB_M <= target_get_vb_version() &&
            is_secure_boot_enable()) {
                if((write_device_info_rpmb((void*) info, PAGE_SIZE)) < 0)
                    ASSERT(0);
        }
        else
            write_device_info_mmc(info);
#else
        write_device_info_mmc(info);
#endif
        free(info);

高通平台默认没有用USE_RPMB_FOR_DEVINFO,一般用的是devinfo分区来存放这两个lock标记:

void write_device_info_mmc(device_info *dev)
{
    ...
    if (devinfo_present)
        index = partition_get_index("devinfo");
    else
        index = partition_get_index("aboot");

    ptn = partition_get_offset(index);
    ...
    if (devinfo_present)
        ret = mmc_write(ptn, device_info_sz, (void *)info_buf);
    else
        ret = mmc_write((ptn + size - device_info_sz), device_info_sz, (void *)info_buf);

可见,就写在devinfo分区开始处, 如果没有devinfo分区,那就放到aboot分区最后。

一般还会有个标记判断是否允许解锁,就是is_allow_unlock,这个标记写在config or frq分区:

static char frp_ptns[2][8] = {"config","frp"};
static int write_allow_oem_unlock(bool allow_unlock)
{
...
    index = partition_get_index(frp_ptns[0]);
    if (index == INVALID_PTN)
    {
        index = partition_get_index(frp_ptns[1]);
        if (index == INVALID_PTN)
        {
            dprintf(CRITICAL, "Neither '%s' nor '%s' partition found\n", frp_ptns[0],frp_ptns[1]);
            return -1;
        }
    }

    ptn = partition_get_offset(index);
    ptn_size = partition_get_size(index);
    offset = ptn_size - blocksize;
    mmc_set_lun(partition_get_lun(index));

    if (mmc_read(ptn + offset, (void *)buf, blocksize))
    {
        dprintf(CRITICAL, "Reading MMC failed\n");
        return -1;
    }

一般分区表里是config分区,写在最后一个block的最后一个byte。

有时会遇到如下错误:

fastboot oem unlock-go
...
FAILED <remote: oem unlock is not allowed)

就是这个标记了:

void cmd_oem_unlock_go(const char *arg, void *data, unsigned sz)
{
    if(!device.is_unlocked) {
        if(!is_allow_unlock) {
            fastboot_fail("oem unlock is not allowed");
            return;
        }

setting开发者选项里有个"打开OEM解锁",其实就是这个标记,让我们确认下:

packages/apps/Settings/src/com/android/settings/development/OemUnlockPreferenceController.java:

    public void onOemUnlockConfirmed() {
        mOemLockManager.setOemUnlockAllowedByUser(true);
    }

frameworks/base/core/java/android/service/oemlock/OemLockManager.java:

    /** 
     * Sets whether the user has allowed this device to be unlocked.
     *
     * All actors involved must agree for OEM unlock to be possible.
     *
     * @param allowed Whether the device should be allowed to be unlocked.
     * @throws SecurityException if the user is not allowed to unlock the device.
     *
     * @see #isOemUnlockAllowedByUser()
     */
    @RequiresPermission(android.Manifest.permission.MANAGE_USER_OEM_UNLOCK_STATE)
    public void setOemUnlockAllowedByUser(boolean allowed) {
        try {
            mService.setOemUnlockAllowedByUser(allowed);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }   
    }

service frameworks/base/services/core/java/com/android/server/oemlock/OemLockService.java:

        // The user has the final say so if they allow unlock, then the device allows the bootloader
        // to OEM unlock it.
        @Override
        public void setOemUnlockAllowedByUser(boolean allowedByUser) {
            if (ActivityManager.isUserAMonkey()) {
                // Prevent a monkey from changing this
                return;
            }   

            enforceManageUserOemUnlockPermission();
            enforceUserIsAdmin();

            final long token = Binder.clearCallingIdentity();
            try {
                if (!isOemUnlockAllowedByAdmin()) {
                    throw new SecurityException("Admin does not allow OEM unlock");
                }   

                if (!mOemLock.isOemUnlockAllowedByCarrier()) {
                    throw new SecurityException("Carrier does not allow OEM unlock");
                }   

                mOemLock.setOemUnlockAllowedByDevice(allowedByUser);
                setPersistentDataBlockOemUnlockAllowedBit(allowedByUser);
            } finally {
                Binder.restoreCallingIdentity(token);
            }   
        }

check setPersistentDataBlockOemUnlockAllowedBit:

    /** 
     * Always synchronize the OemUnlockAllowed bit to the FRP partition, which
     * is used to erase FRP information on a unlockable device.
     */
    private void setPersistentDataBlockOemUnlockAllowedBit(boolean allowed) {
        final PersistentDataBlockManagerInternal pdbmi
                = LocalServices.getService(PersistentDataBlockManagerInternal.class);
        // if mOemLock is PersistentDataBlockLock, then the bit should have already been set
        if (pdbmi != null && !(mOemLock instanceof PersistentDataBlockLock)) {
            Slog.i(TAG, "Update OEM Unlock bit in pst partition to " + allowed);
            pdbmi.forceOemUnlockEnabled(allowed);
        }   
    }

ok, 其实这里已经能看出来是和分区(frp)打交道了,继续跟:

frameworks/base/services/core/java/com/android/server/PersistentDataBlockService.java:

        @Override
        public void forceOemUnlockEnabled(boolean enabled) {
            synchronized (mLock) {
                doSetOemUnlockEnabledLocked(enabled);
                computeAndWriteDigestLocked();
            }   
        }  
    private void doSetOemUnlockEnabledLocked(boolean enabled) {
        FileOutputStream outputStream;
        try {
            outputStream = new FileOutputStream(new File(mDataBlockFile));
        } catch (FileNotFoundException e) {
            Slog.e(TAG, "partition not available", e); 
            return;
        }   

    // tj: 找到这个config分区并写入1 or 0
        try {
            FileChannel channel = outputStream.getChannel();

            channel.position(getBlockDeviceSize() - 1); 

            ByteBuffer data = ByteBuffer.allocate(1);
            data.put(enabled ? (byte) 1 : (byte) 0); 
            data.flip();
            channel.write(data);
            outputStream.flush();
        } catch (IOException e) {
            Slog.e(TAG, "unable to access persistent partition", e); 
            return;
        } finally {
            SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
            IoUtils.closeQuietly(outputStream);
        }
    }

mDataBlockFile是啥?文件开头:

public class PersistentDataBlockService extends SystemService {
    private static final String TAG = PersistentDataBlockService.class.getSimpleName();

    private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
...

    public PersistentDataBlockService(Context context) {
        super(context);
        mContext = context;
        mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
        mBlockDeviceSize = -1; // Load lazily
    }

看下这个属性:

xxx:/ # getprop ro.frp.pst
/dev/block/bootdevice/by-name/config

就是config分区嘛,和bootloader对应上了,找到这个分区后然后把enable写进去, clear :]

是否allow unlock有个属性可以看:

xxx:/ # getprop | grep oem_unlock
[sys.oem_unlock_allowed]: [0]

我们再看下启动时aboot_init call read_device_info对lock的处理:

#if USE_RPMB_FOR_DEVINFO
        if (VB_M <= target_get_vb_version() &&
            is_secure_boot_enable()) {
                if((read_device_info_rpmb((void*) info, PAGE_SIZE)) < 0)
                    ASSERT(0);
        }
        else
            read_device_info_mmc(info);
#else
        read_device_info_mmc(info);
#endif

        if (memcmp(info->magic, DEVICE_MAGIC, DEVICE_MAGIC_SIZE))
        {
            memcpy(info->magic, DEVICE_MAGIC, DEVICE_MAGIC_SIZE);
            if (is_secure_boot_enable()) {
                info->is_unlocked = 0;
#if VERIFIED_BOOT || VERIFIED_BOOT_2
                if (VB_M <= target_get_vb_version())
                    info->is_unlock_critical = 0;
#endif
            } else {
                info->is_unlocked = 1;
#if VERIFIED_BOOT || VERIFIED_BOOT_2
                if (VB_M <= target_get_vb_version())
                    info->is_unlock_critical = 1;
#endif
            }
            info->is_tampered = 0;
            info->charger_screen_enabled = 0;
#if VERIFIED_BOOT || VERIFIED_BOOT_2
            if (VB_M <= target_get_vb_version())
                info->verity_mode = 1; //enforcing by default
#endif
            write_device_info(info);
        }
        memcpy(dev, info, sizeof(device_info));
        free(info);
    }

在从devinfo分区获取magic看是否对得上,如果对不上就给个默认的,secure boot默认就是lock了, non secure boot就默认unlock。对不上比如你擦了这个devinfo or第一次烧录,对的上只有写过这个magic,memcmp返回0进不去。

当然也可以保留magic恢复lock标记:

fastboot oem lock

allow unlock默认是0,擦了后是0,所以erase config没用,你得进系统setting勾选 or 做个allow unlock的img刷进去也行。

is_unlock_critical开关命令:

{"flashing lock_critical", cmd_flashing_lock_critical},
{"flashing unlock_critical", cmd_flashing_unlock_critical},
...
void cmd_flashing_lock_critical(const char *arg, void *data, unsigned sz)
{
    set_device_unlock(UNLOCK_CRITICAL, FALSE);
}

void cmd_flashing_unlock_critical(const char *arg, void *data, unsigned sz)
{
    set_device_unlock(UNLOCK_CRITICAL, TRUE);
}

unlock状态查询命令:

D:\>fastboot oem device-info
...
(bootloader)    Device tampered: false
(bootloader)    Device unlocked: true
(bootloader)    Device critical unlocked: true
(bootloader)    Charger screen enabled: false
(bootloader)    Display panel:
OKAY [  0.054s]
finished. total time: 0.055s