概述
OEM鎖可以禁止用戶刷新bootloader或設備分區,運營商和設備本身都對是否允許OEM解鎖有發言權,並且雙方都必須同意允許這樣做才能使解鎖成為可能。
1. oemlock hal的接口
// 返回HAL的vendor特定標識符。
// 返回的名稱不能由框架解釋,而必須傳遞給vendor的代碼,vendor的代碼可以使用它來標識setOemUnlockAllowedByCarrier使用的安全協議。這使供應商無需維護設備到協議的映射即可識別協議。
// 返回值name表示實現的名字
1. getName()
// 更新運營商是否允許oem unlock
// 該實現可能需要供應商定義的簽名來證明此請求的有效性,以增強其安全性。
// 參數allowed:flag的新值
// 參數signature:簽名以證明此請求的有效性;如果不需要,則為空。
// 返回值status:如果成功更新了標志,則狀態為OK;如果需要簽名,但提供了錯誤的簽名,則狀態為INVALID_SIGNATURE;否則,如果更新失敗,則狀態為FAILED。
2. setOemUnlockAllowedByCarrier(bool allowed, vec<uint8_t> signature)
// 返回運營商是否已經允許oem unlock了
// 返回值status:如果flag被成功讀取,則為OK
// 返回值allowed:flag現在的狀態
3. isOemUnlockAllowedByCarrier()
// 更新設備是否允許oem unlock
// 參數allowed:flag的新值
// 返回值status:如果flag被成功更新,則為OK
frp設1,並算sha-256寫進去就可以了
4. setOemUnlockAllowedByDevice(bool allowed)
// 返回設備是否已經允許oem unlock了
// 返回值status:如果flag被成功讀取,則為OK
// 返回值allowed:flag現在的狀態
讀frp是否為1
5. isOemUnlockAllowedByDevice()
源碼解析
1. Settings源碼解析
android/packages/apps/Settings/src/com/android/settings/development/OemUnlockPreferenceController.java
主要問題是開發者選項中,oemunlock選項為灰色
1.1 updateState -- oemunlock選項為灰色
@Override
protected void onDeveloperOptionsSwitchEnabled() {
handleDeveloperOptionsToggled();
}
private void handleDeveloperOptionsToggled() {
// 打開開發者選項的時候,也會判斷是否要設為灰色(已經unlock的就為灰色了),no_oem_unlock=true也為灰色
mPreference.setEnabled(enableOemUnlockPreference());
if (mPreference.isEnabled()) {
// Check restriction, disable mEnableOemUnlock and apply policy transparency.
mPreference.checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET);
}
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
// oemunlock選項是否已經unlocked了。unlocked的話,這里返回true
mPreference.setChecked(isOemUnlockedAllowed());
updateOemUnlockSettingDescription();
// Showing mEnableOemUnlock preference as device has persistent data block.
mPreference.setDisabledByAdmin(null);
// oemunlock 選項是否要設為灰色(已經unlock的就為灰色了)
mPreference.setEnabled(enableOemUnlockPreference());
if (mPreference.isEnabled()) {
// Check restriction, disable mEnableOemUnlock and apply policy transparency.
mPreference.checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET);
}
}
1.2 enableOemUnlockPreference -- 灰色
private boolean enableOemUnlockPreference() {
// 判斷設備是否已經unlock了,已經unlock則直接返回true,所以已經unlock了的話,這個preference是灰色的
// 如果在overlay中配置了config_defaultFirstUserRestrictions的話,no_oem_unlock=true,對應
// 聯網之后,no_oem_unlock就被設為false了,然后開發者選項中就不為灰色了
return !isBootloaderUnlocked() && isOemUnlockAllowedByUserAndCarrier();
}
1.3 isBootloaderUnlocked
boolean isBootloaderUnlocked() {
// 判斷是否已經unlock了,判斷ro.boot.flash.locked屬性是否為0,為0則是unlock的,unlock則返回true;為1就是locked的(false)
return mOemLockManager.isDeviceOemUnlocked();
}
1.4 isOemUnlockAllowedByUserAndCarrier
boolean isOemUnlockAllowedByUserAndCarrier() {
final UserHandle userHandle = UserHandle.of(UserHandle.myUserId());
// 這里返回true
return mOemLockManager.isOemUnlockAllowedByCarrier()
// 這里一般是true的
&& !mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET,
userHandle);
}
1.5 isOemUnlockedAllowed
@VisibleForTesting
boolean isOemUnlockedAllowed() {
return mOemLockManager.isOemUnlockAllowed();
}
1.6 onPreferenceChange -- 開發者選項打開oemlock選項
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean isUnlocked = (Boolean) newValue;
if (isUnlocked) {
if (!showKeyguardConfirmation(mContext.getResources(),
REQUEST_CODE_ENABLE_OEM_UNLOCK)) {
// 彈出提示框,然后enable的話,就往frp分區寫1
confirmEnableOemUnlock();
}
} else {
mOemLockManager.setOemUnlockAllowedByUser(false);
OemLockInfoDialog.show(mFragment);
}
return true;
}
1.7 confirmEnableOemUnlock
@VisibleForTesting
void confirmEnableOemUnlock() {
EnableOemUnlockSettingWarningDialog.show(mFragment);
}
@Override
public void onClick(DialogInterface dialog, int which) {
final OemUnlockDialogHost host = (OemUnlockDialogHost) getTargetFragment();
if (host == null) {
return;
}
if (which == DialogInterface.BUTTON_POSITIVE) {
host.onOemUnlockDialogConfirmed();
} else {
host.onOemUnlockDialogDismissed();
}
}
1.8 onOemUnlockDialogConfirmed
@Override
public void onOemUnlockDialogConfirmed() {
final OemUnlockPreferenceController controller = getDevelopmentOptionsController(
OemUnlockPreferenceController.class);
controller.onOemUnlockConfirmed();
}
1.9 onOemUnlockConfirmed
public void onOemUnlockConfirmed() {
// oemlock hal寫1
// frp分區寫1。兩步都會去做
mOemLockManager.setOemUnlockAllowedByUser(true);
}
2. OemLockManager
2.1 run - SystemServer
frameworks/base/services/java/com/android/server/SystemServer.java
private void run() {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
try {
.....
SystemServiceRegistry.sEnableServiceNotFoundWtf = true;
....
}
}
2.2 SystemServiceRegistry - static代碼塊
// 類被加載了不一定就會執行靜態代碼塊,只有一個類被主動使用的時候,靜態代碼才會被執行!類被調用了,才會調用static代碼塊
static {
registerService(Context.OEM_LOCK_SERVICE, OemLockManager.class,
new StaticServiceFetcher<OemLockManager>() {
@Override
public OemLockManager createService() throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.OEM_LOCK_SERVICE);
IOemLockService oemLockService = IOemLockService.Stub.asInterface(b);
if (oemLockService != null) {
return new OemLockManager(oemLockService);
} else {
// not supported
return null;
}
}});
}
2.3 isDeviceOemUnlocked
OemLockManager的函數調用都是調用OemLockService里面去的,oemlockmanager只是個接口
public boolean isDeviceOemUnlocked() {
try {
return mService.isDeviceOemUnlocked();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
3. OemLockService
/frameworks/base/services/core/java/com/android/server/oemlock/OemLockService.java
3.1 isDeviceOemUnlocked
@Override
public boolean isDeviceOemUnlocked() {
enforceOemUnlockReadPermission();
String locked = SystemProperties.get(FLASH_LOCK_PROP);
switch (locked) {
// 這個是0,返回的是true
case FLASH_LOCK_UNLOCKED:
return true;
default:
return false;
}
}
3.2 構造函數
private static OemLock getOemLock(Context context) {
final IOemLock oemLockHal = VendorLock.getOemLockHalService();
// 是否使用oemlock hal;如果使用了oemlock hal,則使用oemlock hal
if (oemLockHal != null) {
Slog.i(TAG, "Using vendor lock via the HAL");
return new VendorLock(context, oemLockHal);
} else {
Slog.i(TAG, "Using persistent data block based lock");
//否則的話,就使用PersistentDataBlockLock
return new PersistentDataBlockLock(context);
}
}
public OemLockService(Context context) {
this(context, getOemLock(context));
}
OemLockService(Context context, OemLock oemLock) {
super(context);
mContext = context;
mOemLock = oemLock;
LocalServices.getService(UserManagerInternal.class)
.addUserRestrictionsListener(mUserRestrictionsListener);
}
3.3 isOemUnlockAllowedByCarrier
@Override
public boolean isOemUnlockAllowedByCarrier() {
enforceManageCarrierOemUnlockPermission();
final long token = Binder.clearCallingIdentity();
try {
return mOemLock.isOemUnlockAllowedByCarrier();
} finally {
Binder.restoreCallingIdentity(token);
}
}
3.4 isOemUnlockAllowed
/** Currently MasterClearConfirm will call isOemUnlockAllowed()
* to sync PersistentDataBlockOemUnlockAllowedBit which
* is needed before factory reset
* TODO: Figure out better place to run sync e.g. adding new API
*/
@Override
public boolean isOemUnlockAllowed() {
enforceOemUnlockReadPermission();
final long token = Binder.clearCallingIdentity();
try {
// no_oem_unlock=false;所以isOemUnlockAllowedByCarrier為true
boolean allowed = mOemLock.isOemUnlockAllowedByCarrier()
// 所以主要看isOemUnlockAllowedByDevice:讀/dev/block/by-name/frp分區的最后一位,看是否為1,為1則返回true
&& mOemLock.isOemUnlockAllowedByDevice();
// 往frp分區最后一位寫allowed
setPersistentDataBlockOemUnlockAllowedBit(allowed);
return allowed;
} finally {
Binder.restoreCallingIdentity(token);
}
}
3.5 setOemUnlockAllowedByUser
// 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");
}
// 如果用的是persistentdata的話,下面兩個函數都是一樣的功能,都是往frp分區寫1
mOemLock.setOemUnlockAllowedByDevice(allowedByUser);
// 往frp分區寫1
setPersistentDataBlockOemUnlockAllowedBit(allowedByUser);
} finally {
Binder.restoreCallingIdentity(token);
}
}
4. PersistentDataBlockLock
framework/base/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java
4.1 isOemUnlockAllowedByCarrier
@Override
// no_oem_unlock=true則返回false;no_oem_unlock=false則返回true
boolean isOemUnlockAllowedByCarrier() {
return !UserManager.get(mContext)
.hasUserRestriction(UserManager.DISALLOW_OEM_UNLOCK, UserHandle.SYSTEM);
}
4.2 isOemUnlockAllowedByDevice
@Override
boolean isOemUnlockAllowedByDevice() {
final PersistentDataBlockManager pdbm = (PersistentDataBlockManager)
mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
if (pdbm == null) {
Slog.w(TAG, "PersistentDataBlock is not supported on this device");
return false;
}
return pdbm.getOemUnlockEnabled();
}
4.3 setOemUnlockAllowedByDevice
@Override
void setOemUnlockAllowedByDevice(boolean allowedByDevice) {
// The method name is misleading as it really just means whether or not the device can be
// unlocked but doesn't actually do any unlocking.
final PersistentDataBlockManager pdbm = (PersistentDataBlockManager)
mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
if (pdbm == null) {
Slog.w(TAG, "PersistentDataBlock is not supported on this device");
return;
}
pdbm.setOemUnlockEnabled(allowedByDevice);
}
5. UserManager
android/frameworks/base/core/java/android/os/UserManager.java
5.1 hasUserRestriction
@UnsupportedAppUsage
public boolean hasUserRestriction(@UserRestrictionKey String restrictionKey,
UserHandle userHandle) {
return hasUserRestrictionForUser(restrictionKey, userHandle);
}
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean hasUserRestrictionForUser(@NonNull @UserRestrictionKey String restrictionKey,
@NonNull UserHandle userHandle) {
try {
return mService.hasUserRestriction(restrictionKey, userHandle.getIdentifier());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
6. UserManagerService
frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java
6.1 hasUserRestriction
/** @return a specific user restriction that's in effect currently. */
@Override
public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) {
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "hasUserRestriction");
return mLocalService.hasUserRestriction(restrictionKey, userId);
}
@Override
public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) {
// 檢查是否有這個restrictionKey,里面有一個USER_RESTRICTIONS集合,包含所有的restrictionKey
// 經查看,是有DISALLOW_OEM_UNLOCK這個key的
if (!UserRestrictionsUtils.isValidRestriction(restrictionKey)) {
return false;
}
Bundle restrictions = getEffectiveUserRestrictions(userId);
// 獲取key的值
return restrictions != null && restrictions.getBoolean(restrictionKey);
}
6.2 restrictionKey的更新流程 - 第一次開機
調用棧:
UserManagerService: writeUserLP com.android.server.pm.UserManagerService$UserData@d9662bc called at
UserManagerService: com.android.server.pm.UserManagerService.writeUserLP:2823
UserManagerService: com.android.server.pm.UserManagerService.fallbackToSingleUserLP:2791
UserManagerService: com.android.server.pm.UserManagerService.readUserListLP:2496
UserManagerService: com.android.server.pm.UserManagerService.<init>:624
UserManagerService: com.android.server.pm.UserManagerService.<init>:602
UserManagerService: com.android.server.pm.PackageManagerService.lambda$main$2:2595
UserManagerService: com.android.server.pm.-$$Lambda$PackageManagerService$xKD6SB7pISjc29qfmXIq5O_3OJw.produce:12
UserManagerService: com.android.server.pm.PackageManagerService$Injector$Singleton.get:910
UserManagerService: com.android.server.pm.PackageManagerService$Injector.getUserManagerService:1004
UserManagerService: com.android.server.pm.PackageManagerService.<init>:2871
6.2.1 fallbackToSingleUserLP
@GuardedBy({"mPackagesLock", "mRestrictionsLock"})
private void fallbackToSingleUserLP() {
int flags = UserInfo.FLAG_SYSTEM | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN
| UserInfo.FLAG_PRIMARY;
// Create the system user
String systemUserType = UserManager.isHeadlessSystemUserMode() ?
UserManager.USER_TYPE_SYSTEM_HEADLESS : UserManager.USER_TYPE_FULL_SYSTEM;
flags |= mUserTypes.get(systemUserType).getDefaultUserInfoFlags();
UserInfo system = new UserInfo(UserHandle.USER_SYSTEM, null, null, flags, systemUserType);
UserData userData = putUserInfo(system);
mNextSerialNumber = MIN_USER_ID;
mUserVersion = USER_VERSION;
mUserTypeVersion = UserTypeFactory.getUserTypeVersion();
Bundle restrictions = new Bundle();
try {
// 在這里會解析config_defaultFirstUserRestrictions配置
final String[] defaultFirstUserRestrictions = mContext.getResources().getStringArray(
com.android.internal.R.array.config_defaultFirstUserRestrictions);
for (String userRestriction : defaultFirstUserRestrictions) {
if (UserRestrictionsUtils.isValidRestriction(userRestriction)) {
// 然后給他寫上true,所以no_oem_unlock=true
restrictions.putBoolean(userRestriction, true);
}
}
} catch (Resources.NotFoundException e) {
Slog.e(LOG_TAG, "Couldn't find resource: config_defaultFirstUserRestrictions", e);
}
if (!restrictions.isEmpty()) {
synchronized (mRestrictionsLock) {
mBaseUserRestrictions.updateRestrictions(UserHandle.USER_SYSTEM,
restrictions);
}
}
updateUserIds();
initDefaultGuestRestrictions();
// 然后這里寫到了/data/system/users/0.xml文件
writeUserLP(userData);
writeUserListLP();
}
6.2.2 /data/system/users/0.xml文件
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<user id="0" serialNumber="0" flags="3091" type="android.os.usertype.full.SYSTEM" created="0" lastLoggedIn="1620875315872" lastLoggedInFingerprint="Allwinner/ceres_b4/ceres-b4:11/RP1A.201005.006/eng.xx.20210512.114843:userdebug/test-keys" profileBadge="0">
<restrictions no_oem_unlock="true" />
<device_policy_local_restrictions />
</user>
6.3 restrictionKey的更新流程 - GMS core包更新
調用棧:
UserManagerService: scheduleWriteUser called at
UserManagerService: com.android.server.pm.UserManagerService.scheduleWriteUser:2801
UserManagerService: com.android.server.pm.UserManagerService.updateUserRestrictionsInternalLR:2104
UserManagerService: com.android.server.pm.UserManagerService.setUserRestriction:2076
UserManagerService: android.os.IUserManager$Stub.onTransact:1165
UserManagerService: android.os.Binder.execTransactInternal:1154
UserManagerService: android.os.Binder.execTransact:1123
UserManagerService: <bottom of call stack>
UserManagerService: <bottom of call stack>
UserManagerService: <bottom of call stack>
UserManagerService: <bottom of call stack>
UserManagerService: Applying user restrictions: userId=0 new=Bundle[{no_oem_unlock=false}] prev=Bundle[{no_oem_unlock=true}] called at
UserManagerService: com.android.server.pm.UserManagerService.updateUserRestrictionsInternalLR:2114
UserManagerService: com.android.server.pm.UserManagerService.setUserRestriction:2076
UserManagerService: android.os.IUserManager$Stub.onTransact:1165
UserManagerService: android.os.Binder.execTransactInternal:1154
UserManagerService: android.os.Binder.execTransact:1123
UserManagerService: <bottom of call stack>
UserManagerService: <bottom of call stack>
UserManagerService: <bottom of call stack>
UserManagerService: <bottom of call stack>
UserManagerService: <bottom of call stack>
UserManagerService: writeUserLP com.android.server.pm.UserManagerService$UserData@d9662bc called at
UserManagerService: com.android.server.pm.UserManagerService.writeUserLP:2823
UserManagerService: com.android.server.pm.UserManagerService.access$1500:155
UserManagerService: com.android.server.pm.UserManagerService$MainHandler.handleMessage:4843
UserManagerService: android.os.Handler.dispatchMessage:106
UserManagerService: android.os.Looper.loop:223
UserManagerService: com.android.server.SystemServer.run:644
UserManagerService: com.android.server.SystemServer.main:419
UserManagerService: java.lang.reflect.Method.invoke:-2
UserManagerService: com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run:592
UserManagerService: com.android.internal.os.ZygoteInit.main:1062
大概的流程就是:
聯網 -> GMS Core app調用setUserRestriction接口,設置no_oem_unlock=false -> updateUserRestrictionsInternalLR -> scheduleWriteUser -> writeUserLP:寫到/data/system/users/0.xml文件
7. PersistentDataBlockService
frameworks/base/services/core/java/com/android/server/PersistentDataBlockService.java
7.1 getOemUnlockEnabled
@Override
public boolean getOemUnlockEnabled() {
enforceOemUnlockReadPermission();
return doGetOemUnlockEnabled();
}
7.2 doGetOemUnlockEnabled
private boolean doGetOemUnlockEnabled() {
DataInputStream inputStream;
try {
// 讀"ro.frp.pst"屬性:/dev/block/by-name/frp
inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
} catch (FileNotFoundException e) {
Slog.e(TAG, "partition not available");
return false;
}
try {
synchronized (mLock) {
// 讀/dev/block/by-name/frp分區的最后一位,看是否為1,為1則返回true
inputStream.skip(getBlockDeviceSize() - 1);
return inputStream.readByte() != 0;
}
} catch (IOException e) {
Slog.e(TAG, "unable to access persistent partition", e);
return false;
} finally {
IoUtils.closeQuietly(inputStream);
}
}
7.3 setOemUnlockEnabled
@Override
public void setOemUnlockEnabled(boolean enabled) throws SecurityException {
// do not allow monkey to flip the flag
if (ActivityManager.isUserAMonkey()) {
return;
}
enforceOemUnlockWritePermission();
enforceIsAdmin();
if (enabled) {
// Do not allow oem unlock to be enabled if it's disallowed by a user restriction.
enforceUserRestriction(UserManager.DISALLOW_OEM_UNLOCK);
enforceUserRestriction(UserManager.DISALLOW_FACTORY_RESET);
}
synchronized (mLock) {
// 往frp寫1
doSetOemUnlockEnabledLocked(enabled);
// 計算digest值
computeAndWriteDigestLocked();
}
}
7.4 frp分區的組成
* The persistent data block is currently laid out as follows:
* | ---------BEGINNING OF PARTITION-------------|
* | Partition digest (32 bytes) |
* | --------------------------------------------|
* | PARTITION_TYPE_MARKER (4 bytes) |
* | --------------------------------------------|
* | FRP data block length (4 bytes) |
* | --------------------------------------------|
* | FRP data (variable length) |
* | --------------------------------------------|
* | ... |
* | --------------------------------------------|
* | Test mode data block (10000 bytes) |
* | --------------------------------------------|
* | | Test mode data length (4 bytes) |
* | --------------------------------------------|
* | | Test mode data (variable length) |
* | | ... |
* | --------------------------------------------|
* | FRP credential handle block (1000 bytes) |
* | --------------------------------------------|
* | | FRP credential handle length (4 bytes)|
* | --------------------------------------------|
* | | FRP credential handle (variable len) |
* | | ... |
* | --------------------------------------------|
* | OEM Unlock bit (1 byte) |
* | ---------END OF PARTITION-------------------|
// 最后一位就是oem unlock bit
7.5 doSetOemUnlockEnabledLocked
private void doSetOemUnlockEnabledLocked(boolean enabled) {
FileOutputStream outputStream;
try {
outputStream = getBlockOutputStream();
} catch (IOException e) {
Slog.e(TAG, "partition not available", e);
return;
}
try {
FileChannel channel = outputStream.getChannel();
channel.position(getBlockDeviceSize() - 1);
ByteBuffer data = ByteBuffer.allocate(1);
// 這里判斷是否為1
data.put(enabled ? (byte) 1 : (byte) 0);
data.flip();
// 往frp分區寫
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);
}
}
7.6 computeAndWriteDigestLocked
private boolean computeAndWriteDigestLocked() {
byte[] digest = computeDigestLocked(null);
if (digest != null) {
DataOutputStream outputStream;
try {
outputStream = new DataOutputStream(getBlockOutputStream());
} catch (IOException e) {
Slog.e(TAG, "partition not available?", e);
return false;
}
try {
// 寫digest
outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
outputStream.flush();
} catch (IOException e) {
Slog.e(TAG, "failed to write block checksum", e);
return false;
} finally {
IoUtils.closeQuietly(outputStream);
}
return true;
} else {
return false;
}
}
// 計算digest
private byte[] computeDigestLocked(byte[] storedDigest) {
DataInputStream inputStream;
try {
inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
} catch (FileNotFoundException e) {
Slog.e(TAG, "partition not available?", e);
return null;
}
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
// won't ever happen -- every implementation is required to support SHA-256
Slog.e(TAG, "SHA-256 not supported?", e);
IoUtils.closeQuietly(inputStream);
return null;
}
try {
if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) {
inputStream.read(storedDigest);
} else {
inputStream.skipBytes(DIGEST_SIZE_BYTES);
}
int read;
byte[] data = new byte[1024];
md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest
while ((read = inputStream.read(data)) != -1) {
md.update(data, 0, read);
}
} catch (IOException e) {
Slog.e(TAG, "failed to read partition", e);
return null;
} finally {
IoUtils.closeQuietly(inputStream);
}
return md.digest();
}
oemlock hal實現
實現的版本也是往frp寫1
#include <openssl/sha.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include "OemLockHidl.h"
namespace android {
namespace hardware {
namespace oemlock {
namespace V1_0 {
namespace implementation {
using ::android::base::ParseInt;
using ::android::base::ReadFileToString;
using ::android::base::ReadFullyAtOffset;
using ::android::base::Split;
using ::android::base::StringPrintf;
using ::android::base::unique_fd;
const std::string FRP_DEV_PATH = "/dev/block/by-name/frp";
#define DIGEST_SIZE_BYTES 32
static int64_t get_file_size(int fd) {
struct stat buf;
int ret = fstat(fd, &buf);
if (ret) return 0;
int64_t computed_size = 0;
if (S_ISREG(buf.st_mode)) {
computed_size = buf.st_size;
} else if (S_ISBLK(buf.st_mode)) {
uint64_t block_device_size = 0;
ret = ioctl(fd, BLKGETSIZE64, &block_device_size);
if (ret) return 0;
computed_size = block_device_size;
}
LOG(INFO) << "oemlock_impl get file size " << computed_size;
return computed_size;
}
Return<void> OemLockHidl::getName(getName_cb _hidl_cb)
{
OemLockStatus status = OemLockStatus::OK;
hidl_string name = "oemlock";
_hidl_cb(status, name);
return Void();
}
Return<OemLockSecureStatus> OemLockHidl::setOemUnlockAllowedByCarrier(bool allowed, const hidl_vec<uint8_t>& signature)
{
(void)allowed;
(void)signature;
OemLockSecureStatus status = OemLockSecureStatus::OK;
return status;
}
Return<void> OemLockHidl::isOemUnlockAllowedByCarrier(isOemUnlockAllowedByCarrier_cb _hidl_cb)
{
bool allowed = true;
OemLockStatus status = OemLockStatus::OK;
_hidl_cb(status, allowed);
return Void();
}
OemLockHidl::OemLockHidl() {}
} // namespace implementation
} // namespace V1_0
} // namespace oemlock
} // namespace hardware
} // namespace androi
1. setOemUnlockAllowedByDevice
Return<OemLockStatus> OemLockHidl::setOemUnlockAllowedByDevice(bool allowed)
{
OemLockStatus status = OemLockStatus::OK;
if (set_oemunlock_allowed_by_device_impl(allowed) < 0) {
LOG(ERROR) << "oemlock setOemUnlockAllowedByDevice fail";
status = OemLockStatus::FAILED;
}
return status;
}
int do_set_oemunlock_allowed_by_device(bool in_allowed)
{
unique_fd fd(TEMP_FAILURE_RETRY(open(FRP_DEV_PATH.c_str(), O_WRONLY | O_CLOEXEC | O_BINARY)));
if (fd < 0) {
LOG(ERROR) << "oemlock do_set_oemunlock_allowed_by_device open file fail";
return -1;
}
int64_t offset = get_file_size(fd) - 1;
if (offset < 0) {
LOG(ERROR) << "oemlock do_set_oemunlock_allowed_by_device get_file_size fail";
return -1;
}
LOG(INFO) << "oemlock_impl write oem unlock flag offset " << offset;
uint8_t flag = in_allowed ? (uint8_t) 1 : (uint8_t) 0;
LOG(INFO) << "oemlock_impl write oem unlock flag is " << flag;
if (lseek(fd, offset, SEEK_SET) < 0) {
LOG(ERROR) << "oemlock do_set_oemunlock_allowed_by_device lseek fail";
return -1;
}
if (write(fd, &flag, 1) < 0) {
LOG(ERROR) << "oemlock do_set_oemunlock_allowed_by_device write fail";
return -1;
}
return 0;
}
int compute_and_write_digest()
{
unique_fd fd(TEMP_FAILURE_RETRY(open(FRP_DEV_PATH.c_str(), O_RDWR | O_CLOEXEC | O_BINARY)));
if (fd < 0) {
LOG(ERROR) << "oemlock compute_and_write_digest open file fail";
return -1;
}
int64_t data_size = get_file_size(fd) - DIGEST_SIZE_BYTES;
if (data_size < 0) {
LOG(ERROR) << "oemlock compute_and_write_digest get_file_size fail";
return -1;
}
std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(data_size);
std::unique_ptr<uint8_t[]> digest = std::make_unique<uint8_t[]>(DIGEST_SIZE_BYTES);
if (ReadFullyAtOffset(fd, data.get(), data_size, DIGEST_SIZE_BYTES) < 0) {
LOG(ERROR) << "oemlock compute_and_write_digest ReadFullyAtOffset fail";
return -1;
}
SHA256_CTX sha256_ctx;
SHA256_Init(&sha256_ctx);
SHA256_Update(&sha256_ctx, data.get(), data_size);
SHA256_Final(digest.get(), &sha256_ctx);
if (lseek(fd, 0, SEEK_SET) < 0) {
LOG(ERROR) << "oemlock compute_and_write_digest lseek fail";
return -1;
}
if (write(fd, digest.get(), DIGEST_SIZE_BYTES) < 0) {
LOG(ERROR) << "oemlock compute_and_write_digest write fail";
return -1;
}
return 0;
}
int set_oemunlock_allowed_by_device_impl(bool in_allowed)
{
if (do_set_oemunlock_allowed_by_device(in_allowed) < 0) {
LOG(ERROR) << "oemlock do_set_oemunlock_allowed_by_device fail";
return -1;
}
if (compute_and_write_digest() < 0) {
LOG(ERROR) << "oemlock compute_and_write_digest fail";
return -1;
}
return 0;
}
1.2 isOemUnlockAllowedByDevice
int is_oemunlock_allowed_by_device_impl(bool *allowed) {
unique_fd fd(TEMP_FAILURE_RETRY(open(FRP_DEV_PATH.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY)));
if (fd < 0) {
LOG(ERROR) << "oemlock is_oemunlock_allowed_by_device_impl open file fail";
return -1;
}
int64_t offset = get_file_size(fd) - 1;
if (offset < 0) {
LOG(ERROR) << "oemlock is_oemunlock_allowed_by_device_impl get_file_size fail";
return -1;
}
LOG(INFO) << "oemlock_impl read oem unlock flag offset " << offset;
int flag;
if (ReadFullyAtOffset(fd, &flag, 1, offset) < 0) {
LOG(ERROR) << "oemlock is_oemunlock_allowed_by_device_impl ReadFullyAtOffset fail";
return -1;
}
LOG(INFO) << "oemlock_impl read oem unlock flag is " << flag;
*allowed = (flag == 1) ? true : false;
return 0;
}
Return<void> OemLockHidl::isOemUnlockAllowedByDevice(isOemUnlockAllowedByDevice_cb _hidl_cb)
{
OemLockStatus status = OemLockStatus::OK;
bool allowed = false;
if (is_oemunlock_allowed_by_device_impl(&allowed) < 0) {
LOG(ERROR) << "oemlock isOemUnlockAllowedByDevice fail";
status = OemLockStatus::FAILED;
}
_hidl_cb(status, allowed);
return Void();
}
問題
1. 開發者選項中,oemunlock選項為灰色
由於GMS要求,要把config_defaultFirstUserRestrictions配置為no_oem_unlock,所以開發者選項為灰色
由於oemlock hal不能寫frp分區(selinux neverallow規則),所以沒辦法實現oemlock hal寫frp分區
所以暫時去掉config_defaultFirstUserRestrictions配置,讓機器不用聯網,就可以在開發者選項中打開oemlock選項
diff --git a/common/overlay/overlay/frameworks/base/core/res/res/values/config.xml b/common/overlay/overlay/frameworks/base/core/res/res/values/config.xml
old mode 100644
new mode 100755
index 61ca324..be9a5b0
--- a/common/overlay/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/common/overlay/overlay/frameworks/base/core/res/res/values/config.xml
@@ -718,4 +718,11 @@
</array>
<bool name="config_shutdownForceScreenOff">true</bool>
+
+ <!-- Package name for the device provisioning package. -->
+ <string name="config_deviceProvisioningPackage">com.google.android.apps.work.oobconfig</string>
+
+ <string-array translatable="false" name="config_defaultFirstUserRestrictions">
+ <item>"no_oem_unlock"</item>
+ </string-array>
</resources>
參考
1. android去除安全模式
https://blog.csdn.net/qq_28534581/article/details/84885773
2. Android Verified Boot淺知分享
https://www.jianshu.com/p/d354280f1f27