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