| /* |
| * Copyright (C) 2016 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.tv.settings.system; |
| |
| import static android.os.UserHandle.USER_SYSTEM; |
| |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.SharedPreferences; |
| import android.content.pm.ResolveInfo; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.RemoteException; |
| import android.os.UserManager; |
| import android.util.Log; |
| |
| import androidx.annotation.VisibleForTesting; |
| import androidx.fragment.app.FragmentActivity; |
| |
| import com.android.internal.widget.ILockSettings; |
| import com.android.internal.widget.LockPatternUtils; |
| import com.android.internal.widget.LockscreenCredential; |
| import com.android.tv.settings.dialog.PinDialogFragment; |
| import com.android.tv.settings.users.RestrictedProfilePinDialogFragment; |
| import com.android.tv.settings.users.RestrictedProfilePinService; |
| |
| import java.util.Objects; |
| |
| /** |
| * Triggered instead of the home screen when user-selected home app isn't encryption aware. |
| */ |
| public class FallbackHome extends FragmentActivity implements PinDialogFragment.ResultListener { |
| |
| private static final String TAG = "FallbackHome"; |
| |
| private Handler mHandler = new Handler() { |
| @Override |
| public void handleMessage(Message msg) { |
| maybeFinish(); |
| } |
| }; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED)); |
| |
| maybeStartPinDialog(); |
| maybeFinish(); |
| } |
| |
| @Override |
| protected void onDestroy() { |
| super.onDestroy(); |
| unregisterReceiver(mReceiver); |
| } |
| |
| private BroadcastReceiver mReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| maybeFinish(); |
| } |
| }; |
| |
| void maybeFinish() { |
| if (isUserUnlocked()) { |
| final Intent homeIntent = new Intent(Intent.ACTION_MAIN) |
| .addCategory(Intent.CATEGORY_HOME); |
| final ResolveInfo homeInfo = getPackageManager().resolveActivity(homeIntent, 0); |
| if (Objects.equals(getPackageName(), homeInfo.activityInfo.packageName)) { |
| Log.d(TAG, "User unlocked but no home; let's hope someone enables one soon?"); |
| mHandler.sendEmptyMessageDelayed(0, 500); |
| } else { |
| Log.d(TAG, "User unlocked and real home found; let's go!"); |
| finish(); |
| } |
| } |
| } |
| |
| /** |
| * If we have file-based encryption and a restricted profile we must request PIN entry on boot. |
| * |
| * Unlike a normal password, the restricted profile PIN is set on USER_OWNER in order to |
| * prevent switching out. Under FBE this means that the underlying USER_SYSTEM will remain |
| * encrypted and in RUNNING_LOCKED state. In order for various system functions to work |
| * we will need to decrypt first. |
| */ |
| @VisibleForTesting |
| void maybeStartPinDialog() { |
| if (isUserUnlocked() || !hasLockscreenSecurity(getUserId()) || !isFileEncryptionEnabled()) { |
| return; |
| } |
| |
| unlockDevice(); |
| } |
| |
| private void unlockDevice() { |
| try (LockscreenCredential pin = getPinFromSharedPreferences()) { |
| if (pin == null || pin.isNone()) { |
| showPinDialogToUnlockDevice(); |
| } else { |
| // Give LockSettings the pin. This unlocks the device. |
| unlockDeviceWithPin(pin); |
| } |
| } |
| } |
| |
| @VisibleForTesting |
| void unlockDeviceWithPin(LockscreenCredential pin) { |
| try { |
| getLockSettings().checkCredential(pin, USER_SYSTEM, null); |
| } catch (RemoteException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| @VisibleForTesting |
| void showPinDialogToUnlockDevice() { |
| // Prompt the user with a dialog to dial the pin and unlock the device. |
| RestrictedProfilePinDialogFragment restrictedProfilePinDialogFragment = |
| RestrictedProfilePinDialogFragment.newInstance( |
| PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN); |
| restrictedProfilePinDialogFragment.show(getSupportFragmentManager(), |
| PinDialogFragment.DIALOG_TAG); |
| } |
| |
| @VisibleForTesting |
| LockscreenCredential getPinFromSharedPreferences() { |
| SharedPreferences sp = getSharedPreferences(RestrictedProfilePinService.PIN_STORE_NAME, |
| Context.MODE_PRIVATE); |
| return LockscreenCredential.createPinOrNone( |
| sp.getString(RestrictedProfilePinService.PIN_STORE_NAME, null)); |
| } |
| |
| @VisibleForTesting |
| boolean isUserUnlocked() { |
| return getSystemService(UserManager.class).isUserUnlocked(); |
| } |
| |
| @VisibleForTesting |
| boolean hasLockscreenSecurity(final int userId) { |
| final LockPatternUtils lpu = new LockPatternUtils(this); |
| return lpu.isLockPasswordEnabled(userId) || lpu.isLockPatternEnabled(userId); |
| } |
| |
| @VisibleForTesting |
| boolean isFileEncryptionEnabled() { |
| return LockPatternUtils.isFileEncryptionEnabled(); |
| } |
| |
| @VisibleForTesting |
| ILockSettings getLockSettings() { |
| return new LockPatternUtils(this).getLockSettings(); |
| } |
| |
| @Override |
| public void pinFragmentDone(int requestCode, boolean success) { |
| maybeStartPinDialog(); |
| maybeFinish(); |
| } |
| } |