一、故障现象
开发者模式中的 OEM unlocking 选项是关闭的,且呈灰色不可用状态;
其Summary显示 ·Connect to internet or contact your carrier.
**
经过一番排查,GMS版本在gms配置之后,联网就可以用了;但非GMS版本却始终用不了。
二、分析此选项的控制逻辑
- 找到布局
\vendor\mediatek\proprietary\packages\apps\MtkSettings\res\xml\development_settings.xml
<com.android.settingslib.RestrictedSwitchPreferenceandroid:key="oem_unlock_enable"android:title="@string/oem_unlock_enable"android:summary="@string/oem_unlock_enable_summary"settings:useAdditionalSummary="true" />
- 找到 Controller
\vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\development\OemUnlockPreferenceController.java
此Controller中,isAvailable() 方法控制此选项是否可见:
@Overridepublic boolean isAvailable() {return mOemLockManager != null;}
根据现象反推,此时 mOemLockManager 必定不为 null。 且 mPreference.setEnabled(enableOemUnlockPreference()) 看出 enableOemUnlockPreference() = false
enableOemUnlockPreference的实现如下:
private boolean enableOemUnlockPreference() {return !isBootloaderUnlocked() && isOemUnlockAllowedByUserAndCarrier();}
再根据Summary信息的控制逻辑:
private void updateOemUnlockSettingDescription() {int oemUnlockSummary = R.string.oem_unlock_enable_summary;if (isBootloaderUnlocked()) {oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_bootloader_unlocked;} else if (isSimLockedDevice()) {oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_sim_locked_device;} else if (!isOemUnlockAllowedByUserAndCarrier()) {// If the device isn't SIM-locked but OEM unlock is disallowed by some party, this// means either some other carrier restriction is in place or the device hasn't been// able to confirm which restrictions (SIM-lock or otherwise) apply.oemUnlockSummary =R.string.oem_unlock_enable_disabled_summary_connectivity_or_locked;}mPreference.setSummary(mContext.getResources().getString(oemUnlockSummary));}
因Summary进了 else if (!isOemUnlockAllowedByUserAndCarrier()),所以说明: isBootloaderUnlocked() = false isOemUnlockAllowedByUserAndCarrier() = false 同时侧面证明了 enableOemUnlockPreference() = false
isOemUnlockAllowedByUserAndCarrier()的实现如下:
/*** Returns whether OEM unlock is allowed by the user and carrier.* This does not take into account any restrictions imposed by the device policy.*/@VisibleForTestingboolean isOemUnlockAllowedByUserAndCarrier() {final UserHandle userHandle = UserHandle.of(UserHandle.myUserId());return mOemLockManager.isOemUnlockAllowedByCarrier()&& !mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET,userHandle);}
在此方法中增加log后打印信息如下:
isOemUnlockAllowedByUserAndCarrier, isOemUnlockAllowedByCarrier=falseisOemUnlockAllowedByUserAndCarrier, hasBaseUserRestriction DISALLOW_FACTORY_RESET=falseisOemUnlockAllowedByUserAndCarrier, return=false
那么问题就在 mOemLockManager.isOemUnlockAllowedByCarrier() = false 上了。
而OemLockManager 对应的 OemLockService,即此Service中isOemUnlockAllowedByCarrier返回的值是false:
// \frameworks\base\services\core\java\com\android\server\oemlock\OemLockService.java@Overridepublic boolean isOemUnlockAllowedByCarrier() {enforceManageCarrierOemUnlockPermission();final long token = Binder.clearCallingIdentity();try {return mOemLock.isOemUnlockAllowedByCarrier();} finally {Binder.restoreCallingIdentity(token);}}
这里Service将调用转给了 OemLock,而OemLock是个抽象类:
// \frameworks\base\services\core\java\com\android\server\oemlock\OemLock.javapackage com.android.server.oemlock;import android.annotation.Nullable;abstract class OemLock {@Nullableabstract String getLockName();abstract void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature);abstract boolean isOemUnlockAllowedByCarrier();abstract void setOemUnlockAllowedByDevice(boolean allowedByDevice);abstract boolean isOemUnlockAllowedByDevice();}
OemLock的实例化是在OemLockService实例化时进行的:
/** Select the OEM lock implementation */private static OemLock getOemLock(Context context) {final IOemLock oemLockHal = VendorLock.getOemLockHalService();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");return new PersistentDataBlockLock(context);}}public OemLockService(Context context) {this(context, getOemLock(context));}
根据开机日志:
OemLock: OemLock HAL not present on deviceOemLock: Using persistent data block based lock
可知,OemLock 的示例对象是 PersistentDataBlockLock,那就是说 PersistentDataBlockLock.isOemUnlockAllowedByCarrier 返回的false.
// \frameworks\base\services\core\java\com\android\server\oemlock\PersistentDataBlockLock.java@Overrideboolean isOemUnlockAllowedByCarrier() {return !UserManager.get(mContext).hasUserRestriction(UserManager.DISALLOW_OEM_UNLOCK, UserHandle.SYSTEM);}
这就说明 UserManager.DISALLOW_OEM_UNLOCK 对应的值是 true
- 分析UserManager
UserManager 对应 UserManagerService,其hasUserRestriction的实现如下:
/** @return a specific user restriction that's in effect currently. */@Overridepublic boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) {checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "hasUserRestriction");return mLocalService.hasUserRestriction(restrictionKey, userId);}
调用转发给内部类 LocalService
@Overridepublic boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) {if (!UserRestrictionsUtils.isValidRestriction(restrictionKey)) {// 因返回值是true,故必定没有进这里return false;}Bundle restrictions = getEffectiveUserRestrictions(userId);return restrictions != null && restrictions.getBoolean(restrictionKey);}
因返回值是true,说明 restrictions!=null, 且 restrictions.getBoolean(restrictionKey) = true, 即 restrictions.getBoolean(UserManager.DISALLOW_OEM_UNLOCK) = true
getEffectiveUserRestrictions的实现如下:
private Bundle getEffectiveUserRestrictions(@UserIdInt int userId) {synchronized (mRestrictionsLock) {Bundle restrictions = mCachedEffectiveUserRestrictions.getRestrictions(userId);// 此处 restrictions 必不为nullif (restrictions == null) {restrictions = computeEffectiveUserRestrictionsLR(userId);mCachedEffectiveUserRestrictions.updateRestrictions(userId, restrictions);}return restrictions;}}
到这里就只需要关注 mCachedEffectiveUserRestrictions 的数据是怎么填入的了。
整个 UserManagerService中,只有如下方法中涉及到填充或者更新(其它地方均为移除或查询):
@GuardedBy("mRestrictionsLock")private void updateUserRestrictionsInternalLR(@Nullable Bundle newBaseRestrictions, @UserIdInt int userId) {// ......final Bundle effective = computeEffectiveUserRestrictionsLR(userId);mCachedEffectiveUserRestrictions.updateRestrictions(userId, effective);// Apply the new restrictions.if (DBG) {debug("Applying user restrictions: userId=" + userId+ " new=" + effective + " prev=" + prevAppliedRestrictions);}// ......}
而 updateUserRestrictionsInternalLR 被调用的地方如下(另一处参数是null,忽略):
@Overridepublic void setUserRestriction(String key, boolean value, @UserIdInt int userId) {checkManageUsersPermission("setUserRestriction");if (!UserRestrictionsUtils.isValidRestriction(key)) {return;}synchronized (mRestrictionsLock) {// Note we can't modify Bundles stored in mBaseUserRestrictions directly, so create// a copy.final Bundle newRestrictions = UserRestrictionsUtils.clone(mBaseUserRestrictions.getRestrictions(userId));newRestrictions.putBoolean(key, value);updateUserRestrictionsInternalLR(newRestrictions, userId);}}
然后在MTK的提示下跟到代码:
@GuardedBy({"mPackagesLock", "mRestrictionsLock"})private void fallbackToSingleUserLP() {// ......Bundle restrictions = new Bundle();try {final String[] defaultFirstUserRestrictions = mContext.getResources().getStringArray(com.android.internal.R.array.config_defaultFirstUserRestrictions);for (String userRestriction : defaultFirstUserRestrictions) {if (UserRestrictionsUtils.isValidRestriction(userRestriction)) {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);}}// ......}
可以看到,这里 config_defaultFirstUserRestrictions 的所有元素都会被 put 为 true。
但此配置默认是没有元素的:
<!-- frameworks/base/core/res/res/values/config.xml --><!-- User restrictions set when the first user is created.Note: Also update appropriate overlay files. --><string-array translatable="false" name="config_defaultFirstUserRestrictions"></string-array>
但另一位同事很早前为了解决CTS Fail修改了这里,增加了:
<item>"no_oem_unlock"</item>
而此值正好是 UserManager.DISALLOW_OEM_UNLOCK 的值。
三、解决方案
还原上述frameworks中的直接修改,在GMS包中建立overlay。
以此达到当编译GMS版本时,自动overlay掉这个配置,达到此前的Fail项也能过;但同时不影响非GMS版本的目的。
