| /* | |
| * 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; | |
| } | |
| } | |
| } |