一、故障现象

开发者模式中的 OEM unlocking 选项是关闭的,且呈灰色不可用状态;
其Summary显示 ·Connect to internet or contact your carrier.
**
经过一番排查,GMS版本在gms配置之后,联网就可以用了;但非GMS版本却始终用不了。

二、分析此选项的控制逻辑

  1. 找到布局

\vendor\mediatek\proprietary\packages\apps\MtkSettings\res\xml\development_settings.xml

  1. <com.android.settingslib.RestrictedSwitchPreference
  2. android:key="oem_unlock_enable"
  3. android:title="@string/oem_unlock_enable"
  4. android:summary="@string/oem_unlock_enable_summary"
  5. settings:useAdditionalSummary="true" />
  1. 找到 Controller

\vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\development\OemUnlockPreferenceController.java
此Controller中,isAvailable() 方法控制此选项是否可见:

  1. @Override
  2. public boolean isAvailable() {
  3. return mOemLockManager != null;
  4. }

根据现象反推,此时 mOemLockManager 必定不为 null。 且 mPreference.setEnabled(enableOemUnlockPreference()) 看出 enableOemUnlockPreference() = false

enableOemUnlockPreference的实现如下:

  1. private boolean enableOemUnlockPreference() {
  2. return !isBootloaderUnlocked() && isOemUnlockAllowedByUserAndCarrier();
  3. }

再根据Summary信息的控制逻辑:

  1. private void updateOemUnlockSettingDescription() {
  2. int oemUnlockSummary = R.string.oem_unlock_enable_summary;
  3. if (isBootloaderUnlocked()) {
  4. oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_bootloader_unlocked;
  5. } else if (isSimLockedDevice()) {
  6. oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_sim_locked_device;
  7. } else if (!isOemUnlockAllowedByUserAndCarrier()) {
  8. // If the device isn't SIM-locked but OEM unlock is disallowed by some party, this
  9. // means either some other carrier restriction is in place or the device hasn't been
  10. // able to confirm which restrictions (SIM-lock or otherwise) apply.
  11. oemUnlockSummary =
  12. R.string.oem_unlock_enable_disabled_summary_connectivity_or_locked;
  13. }
  14. mPreference.setSummary(mContext.getResources().getString(oemUnlockSummary));
  15. }

因Summary进了 else if (!isOemUnlockAllowedByUserAndCarrier()),所以说明: isBootloaderUnlocked() = false isOemUnlockAllowedByUserAndCarrier() = false 同时侧面证明了 enableOemUnlockPreference() = false

isOemUnlockAllowedByUserAndCarrier()的实现如下:

  1. /**
  2. * Returns whether OEM unlock is allowed by the user and carrier.
  3. * This does not take into account any restrictions imposed by the device policy.
  4. */
  5. @VisibleForTesting
  6. boolean isOemUnlockAllowedByUserAndCarrier() {
  7. final UserHandle userHandle = UserHandle.of(UserHandle.myUserId());
  8. return mOemLockManager.isOemUnlockAllowedByCarrier()
  9. && !mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET,
  10. userHandle);
  11. }

在此方法中增加log后打印信息如下:

  1. isOemUnlockAllowedByUserAndCarrier, isOemUnlockAllowedByCarrier=false
  2. isOemUnlockAllowedByUserAndCarrier, hasBaseUserRestriction DISALLOW_FACTORY_RESET=false
  3. isOemUnlockAllowedByUserAndCarrier, return=false

那么问题就在 mOemLockManager.isOemUnlockAllowedByCarrier() = false 上了。
而OemLockManager 对应的 OemLockService,即此Service中isOemUnlockAllowedByCarrier返回的值是false:

  1. // \frameworks\base\services\core\java\com\android\server\oemlock\OemLockService.java
  2. @Override
  3. public boolean isOemUnlockAllowedByCarrier() {
  4. enforceManageCarrierOemUnlockPermission();
  5. final long token = Binder.clearCallingIdentity();
  6. try {
  7. return mOemLock.isOemUnlockAllowedByCarrier();
  8. } finally {
  9. Binder.restoreCallingIdentity(token);
  10. }
  11. }

这里Service将调用转给了 OemLock,而OemLock是个抽象类:

  1. // \frameworks\base\services\core\java\com\android\server\oemlock\OemLock.java
  2. package com.android.server.oemlock;
  3. import android.annotation.Nullable;
  4. abstract class OemLock {
  5. @Nullable
  6. abstract String getLockName();
  7. abstract void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature);
  8. abstract boolean isOemUnlockAllowedByCarrier();
  9. abstract void setOemUnlockAllowedByDevice(boolean allowedByDevice);
  10. abstract boolean isOemUnlockAllowedByDevice();
  11. }

OemLock的实例化是在OemLockService实例化时进行的:

  1. /** Select the OEM lock implementation */
  2. private static OemLock getOemLock(Context context) {
  3. final IOemLock oemLockHal = VendorLock.getOemLockHalService();
  4. if (oemLockHal != null) {
  5. Slog.i(TAG, "Using vendor lock via the HAL");
  6. return new VendorLock(context, oemLockHal);
  7. } else {
  8. Slog.i(TAG, "Using persistent data block based lock");
  9. return new PersistentDataBlockLock(context);
  10. }
  11. }
  12. public OemLockService(Context context) {
  13. this(context, getOemLock(context));
  14. }

根据开机日志:

  1. OemLock: OemLock HAL not present on device
  2. OemLock: Using persistent data block based lock

可知,OemLock 的示例对象是 PersistentDataBlockLock,那就是说 PersistentDataBlockLock.isOemUnlockAllowedByCarrier 返回的false.

  1. // \frameworks\base\services\core\java\com\android\server\oemlock\PersistentDataBlockLock.java
  2. @Override
  3. boolean isOemUnlockAllowedByCarrier() {
  4. return !UserManager.get(mContext)
  5. .hasUserRestriction(UserManager.DISALLOW_OEM_UNLOCK, UserHandle.SYSTEM);
  6. }

这就说明 UserManager.DISALLOW_OEM_UNLOCK 对应的值是 true

  1. 分析UserManager

UserManager 对应 UserManagerService,其hasUserRestriction的实现如下:

  1. /** @return a specific user restriction that's in effect currently. */
  2. @Override
  3. public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) {
  4. checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "hasUserRestriction");
  5. return mLocalService.hasUserRestriction(restrictionKey, userId);
  6. }

调用转发给内部类 LocalService

  1. @Override
  2. public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) {
  3. if (!UserRestrictionsUtils.isValidRestriction(restrictionKey)) {
  4. // 因返回值是true,故必定没有进这里
  5. return false;
  6. }
  7. Bundle restrictions = getEffectiveUserRestrictions(userId);
  8. return restrictions != null && restrictions.getBoolean(restrictionKey);
  9. }

因返回值是true,说明 restrictions!=null, 且 restrictions.getBoolean(restrictionKey) = true, 即 restrictions.getBoolean(UserManager.DISALLOW_OEM_UNLOCK) = true

getEffectiveUserRestrictions的实现如下:

  1. private Bundle getEffectiveUserRestrictions(@UserIdInt int userId) {
  2. synchronized (mRestrictionsLock) {
  3. Bundle restrictions = mCachedEffectiveUserRestrictions.getRestrictions(userId);
  4. // 此处 restrictions 必不为null
  5. if (restrictions == null) {
  6. restrictions = computeEffectiveUserRestrictionsLR(userId);
  7. mCachedEffectiveUserRestrictions.updateRestrictions(userId, restrictions);
  8. }
  9. return restrictions;
  10. }
  11. }

到这里就只需要关注 mCachedEffectiveUserRestrictions 的数据是怎么填入的了。
整个 UserManagerService中,只有如下方法中涉及到填充或者更新(其它地方均为移除或查询):

  1. @GuardedBy("mRestrictionsLock")
  2. private void updateUserRestrictionsInternalLR(
  3. @Nullable Bundle newBaseRestrictions, @UserIdInt int userId) {
  4. // ......
  5. final Bundle effective = computeEffectiveUserRestrictionsLR(userId);
  6. mCachedEffectiveUserRestrictions.updateRestrictions(userId, effective);
  7. // Apply the new restrictions.
  8. if (DBG) {
  9. debug("Applying user restrictions: userId=" + userId
  10. + " new=" + effective + " prev=" + prevAppliedRestrictions);
  11. }
  12. // ......
  13. }

而 updateUserRestrictionsInternalLR 被调用的地方如下(另一处参数是null,忽略):

  1. @Override
  2. public void setUserRestriction(String key, boolean value, @UserIdInt int userId) {
  3. checkManageUsersPermission("setUserRestriction");
  4. if (!UserRestrictionsUtils.isValidRestriction(key)) {
  5. return;
  6. }
  7. synchronized (mRestrictionsLock) {
  8. // Note we can't modify Bundles stored in mBaseUserRestrictions directly, so create
  9. // a copy.
  10. final Bundle newRestrictions = UserRestrictionsUtils.clone(
  11. mBaseUserRestrictions.getRestrictions(userId));
  12. newRestrictions.putBoolean(key, value);
  13. updateUserRestrictionsInternalLR(newRestrictions, userId);
  14. }
  15. }

然后在MTK的提示下跟到代码:

  1. @GuardedBy({"mPackagesLock", "mRestrictionsLock"})
  2. private void fallbackToSingleUserLP() {
  3. // ......
  4. Bundle restrictions = new Bundle();
  5. try {
  6. final String[] defaultFirstUserRestrictions = mContext.getResources().getStringArray(
  7. com.android.internal.R.array.config_defaultFirstUserRestrictions);
  8. for (String userRestriction : defaultFirstUserRestrictions) {
  9. if (UserRestrictionsUtils.isValidRestriction(userRestriction)) {
  10. restrictions.putBoolean(userRestriction, true);
  11. }
  12. }
  13. } catch (Resources.NotFoundException e) {
  14. Slog.e(LOG_TAG, "Couldn't find resource: config_defaultFirstUserRestrictions", e);
  15. }
  16. if (!restrictions.isEmpty()) {
  17. synchronized (mRestrictionsLock) {
  18. mBaseUserRestrictions.updateRestrictions(UserHandle.USER_SYSTEM,
  19. restrictions);
  20. }
  21. }
  22. // ......
  23. }

可以看到,这里 config_defaultFirstUserRestrictions 的所有元素都会被 put 为 true。
但此配置默认是没有元素的:

  1. <!-- frameworks/base/core/res/res/values/config.xml -->
  2. <!-- User restrictions set when the first user is created.
  3. Note: Also update appropriate overlay files. -->
  4. <string-array translatable="false" name="config_defaultFirstUserRestrictions">
  5. </string-array>

但另一位同事很早前为了解决CTS Fail修改了这里,增加了:

  1. <item>"no_oem_unlock"</item>

而此值正好是 UserManager.DISALLOW_OEM_UNLOCK 的值。

三、解决方案

还原上述frameworks中的直接修改,在GMS包中建立overlay。
以此达到当编译GMS版本时,自动overlay掉这个配置,达到此前的Fail项也能过;但同时不影响非GMS版本的目的。