oemlock hal


概述

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM