一般安卓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