一、故障现象
开发者模式中的 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.RestrictedSwitchPreference
android: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() 方法控制此选项是否可见:
@Override
public 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.
*/
@VisibleForTesting
boolean isOemUnlockAllowedByUserAndCarrier() {
final UserHandle userHandle = UserHandle.of(UserHandle.myUserId());
return mOemLockManager.isOemUnlockAllowedByCarrier()
&& !mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET,
userHandle);
}
在此方法中增加log后打印信息如下:
isOemUnlockAllowedByUserAndCarrier, isOemUnlockAllowedByCarrier=false
isOemUnlockAllowedByUserAndCarrier, hasBaseUserRestriction DISALLOW_FACTORY_RESET=false
isOemUnlockAllowedByUserAndCarrier, return=false
那么问题就在 mOemLockManager.isOemUnlockAllowedByCarrier() = false 上了。
而OemLockManager 对应的 OemLockService,即此Service中isOemUnlockAllowedByCarrier返回的值是false:
// \frameworks\base\services\core\java\com\android\server\oemlock\OemLockService.java
@Override
public 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.java
package com.android.server.oemlock;
import android.annotation.Nullable;
abstract class OemLock {
@Nullable
abstract 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 device
OemLock: Using persistent data block based lock
可知,OemLock 的示例对象是 PersistentDataBlockLock,那就是说 PersistentDataBlockLock.isOemUnlockAllowedByCarrier 返回的false.
// \frameworks\base\services\core\java\com\android\server\oemlock\PersistentDataBlockLock.java
@Override
boolean 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. */
@Override
public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) {
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "hasUserRestriction");
return mLocalService.hasUserRestriction(restrictionKey, userId);
}
调用转发给内部类 LocalService
@Override
public 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 必不为null
if (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,忽略):
@Override
public 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版本的目的。