Files
packages_apps_Settings/src/com/android/settings/development/OemUnlockPreferenceController.java
Shawn Willden fb2d3b7abb Catch security exception on isOemUnlockAllowed
As of Android 15, devices with FRP active will throw an exception when loading developer options attempts to update the OEM unlock state, which causes the app to crash.  This CL just catches the exception and reports that OEM unlock is not, in fact, settable.

A full fix should probably tell the user that OEM unlock may not be set because FRP is active, rather than just quietly failing and logging the situation, but because GMS Core is soon going to begin blocking access to the Android UI when FRP is active, meaning that developer options won't even be reachable and the user will be informed about FRP state by GMS Core, the effort that would require isn't justified.

Note that this CL does not add a test for this change because it is not possible for CTS to put the device in FRP-active state to test the relevant case.  Manual testing is required.

Test: SettingsUnitTests
Flag: EXEMPT bugfix
Bug: 405023810
Change-Id: Ic43de93a4208bbc17f2e287d52f9baf281cd678c
2025-03-21 15:46:11 -07:00

249 lines
9.4 KiB
Java

/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes.REQUEST_CODE_ENABLE_OEM_UNLOCK;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.oemlock.OemLockManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
public class OemUnlockPreferenceController extends DeveloperOptionsPreferenceController implements
Preference.OnPreferenceChangeListener, PreferenceControllerMixin, OnActivityResultListener {
private static final String PREFERENCE_KEY = "oem_unlock_enable";
private static final String TAG = "OemUnlockPreferenceController";
private static final String OEM_UNLOCK_SUPPORTED_KEY = "ro.oem_unlock_supported";
private static final String UNSUPPORTED = "-9999";
private static final String SUPPORTED = "1";
private final OemLockManager mOemLockManager;
private final UserManager mUserManager;
private final TelephonyManager mTelephonyManager;
private final Activity mActivity;
@Nullable private final DevelopmentSettingsDashboardFragment mFragment;
private RestrictedSwitchPreference mPreference;
public OemUnlockPreferenceController(Context context, Activity activity,
@Nullable DevelopmentSettingsDashboardFragment fragment) {
super(context);
if (!TextUtils.equals(SystemProperties.get(OEM_UNLOCK_SUPPORTED_KEY, UNSUPPORTED),
SUPPORTED)) {
mOemLockManager = null;
Log.w(TAG, "oem_unlock not supported.");
} else {
mOemLockManager = (OemLockManager) context.getSystemService(Context.OEM_LOCK_SERVICE);
}
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mActivity = activity;
mFragment = fragment;
}
@Override
public boolean isAvailable() {
return mOemLockManager != null;
}
@Override
public String getPreferenceKey() {
return PREFERENCE_KEY;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean isUnlocked = (Boolean) newValue;
if (isUnlocked) {
if (!showKeyguardConfirmation(mContext.getResources(),
REQUEST_CODE_ENABLE_OEM_UNLOCK)) {
confirmEnableOemUnlock();
}
} else {
mOemLockManager.setOemUnlockAllowedByUser(false);
OemLockInfoDialog.show(mFragment);
}
return true;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
mPreference.setChecked(isOemUnlockedAllowed());
updateOemUnlockSettingDescription();
// Showing mEnableOemUnlock preference as device has persistent data block.
mPreference.setDisabledByAdmin(null);
mPreference.setEnabled(enableOemUnlockPreference());
if (mPreference.isEnabled()) {
// Check restriction, disable mEnableOemUnlock and apply policy transparency.
mPreference.checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET);
}
}
@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_ENABLE_OEM_UNLOCK) {
if (resultCode == Activity.RESULT_OK) {
if (mPreference.isChecked()) {
confirmEnableOemUnlock();
} else {
mOemLockManager.setOemUnlockAllowedByUser(false);
}
}
return true;
}
return false;
}
@Override
protected void onDeveloperOptionsSwitchEnabled() {
handleDeveloperOptionsToggled();
}
public void onOemUnlockConfirmed() {
mOemLockManager.setOemUnlockAllowedByUser(true);
}
public void onOemUnlockDismissed() {
if (mPreference == null) {
return;
}
updateState(mPreference);
}
private void handleDeveloperOptionsToggled() {
mPreference.setEnabled(enableOemUnlockPreference());
if (mPreference.isEnabled()) {
// Check restriction, disable mEnableOemUnlock and apply policy transparency.
mPreference.checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET);
}
}
private void updateOemUnlockSettingDescription() {
int oemUnlockSummary = com.android.settingslib.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));
}
/** Returns {@code true} if the device is SIM-locked. Otherwise, returns {@code false}. */
private boolean isSimLockedDevice() {
if (!mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)) {
Log.w(TAG,
"getAllowedCarriers is unsupported without "
+ "PackageManager#FEATURE_TELEPHONY_CARRIERLOCK");
return false;
}
int phoneCount = mTelephonyManager.getPhoneCount();
for (int i = 0; i < phoneCount; i++) {
if (mTelephonyManager.getAllowedCarriers(i).size() > 0) {
return true;
}
}
return false;
}
/**
* Returns {@code true} if the bootloader has been unlocked. Otherwise, returns {code false}.
*/
@VisibleForTesting
boolean isBootloaderUnlocked() {
return mOemLockManager.isDeviceOemUnlocked();
}
private boolean enableOemUnlockPreference() {
return !isBootloaderUnlocked() && isOemUnlockAllowedByUserAndCarrier();
}
@VisibleForTesting
boolean showKeyguardConfirmation(Resources resources, int requestCode) {
final ChooseLockSettingsHelper.Builder builder =
new ChooseLockSettingsHelper.Builder(mActivity, mFragment);
return builder.setRequestCode(requestCode)
.setTitle(resources.getString(com.android.settingslib.R.string.oem_unlock_enable))
.show();
}
@VisibleForTesting
void confirmEnableOemUnlock() {
EnableOemUnlockSettingWarningDialog.show(mFragment);
}
/**
* 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);
}
@VisibleForTesting
boolean isOemUnlockedAllowed() {
try {
return mOemLockManager.isOemUnlockAllowed();
} catch (SecurityException e) {
// This exception is thrown if the device is not allowed to check (or change) the OEM
// unlock setting because Factory Reset Protection is active.
Log.e(TAG, "Failed to check OEM unlock allowed", e);
return false;
}
}
}