From b8be8dff69bf174e4ed41a398018e5cad8128d17 Mon Sep 17 00:00:00 2001
From: t895 <clombardo169@gmail.com>
Date: Wed, 24 Jan 2024 12:37:26 -0500
Subject: [PATCH] android: Add key check

---
 .../java/org/yuzu/yuzu_emu/NativeLibrary.kt   |  5 +++
 .../yuzu/yuzu_emu/fragments/SetupFragment.kt  |  6 ++-
 .../org/yuzu/yuzu_emu/model/HomeViewModel.kt  |  7 ++++
 .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 42 +++++++++++++++++++
 src/android/app/src/main/jni/native.cpp       |  6 +++
 .../app/src/main/res/values/strings.xml       |  3 ++
 6 files changed, 67 insertions(+), 2 deletions(-)

diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index c408485c62..0fb35bf980 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -614,6 +614,11 @@ object NativeLibrary {
      */
     external fun clearFilesystemProvider()
 
+    /**
+     * Checks if all necessary keys are present for decryption
+     */
+    external fun areKeysPresent(): Boolean
+
     /**
      * Button type for use in onTouchEvent
      */
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index 064342cdda..ebf41a6394 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -31,6 +31,7 @@ import androidx.preference.PreferenceManager
 import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
 import com.google.android.material.transition.MaterialFadeThrough
 import kotlinx.coroutines.launch
+import org.yuzu.yuzu_emu.NativeLibrary
 import java.io.File
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.YuzuApplication
@@ -162,7 +163,7 @@ class SetupFragment : Fragment() {
                     R.string.install_prod_keys_warning_help,
                     {
                         val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
-                        if (file.exists()) {
+                        if (file.exists() && NativeLibrary.areKeysPresent()) {
                             StepState.COMPLETE
                         } else {
                             StepState.INCOMPLETE
@@ -347,7 +348,8 @@ class SetupFragment : Fragment() {
     val getProdKey =
         registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
             if (result != null) {
-                if (mainActivity.processKey(result)) {
+                mainActivity.processKey(result)
+                if (NativeLibrary.areKeysPresent()) {
                     keyCallback.onStepCompleted()
                 }
             }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
index 513ac2fc57..cfc777b81c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
@@ -31,6 +31,9 @@ class HomeViewModel : ViewModel() {
     private val _reloadPropertiesList = MutableStateFlow(false)
     val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow()
 
+    private val _checkKeys = MutableStateFlow(false)
+    val checkKeys = _checkKeys.asStateFlow()
+
     var navigatedToSetup = false
 
     fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
@@ -66,4 +69,8 @@ class HomeViewModel : ViewModel() {
     fun reloadPropertiesList(reload: Boolean) {
         _reloadPropertiesList.value = reload
     }
+
+    fun setCheckKeys(value: Boolean) {
+        _checkKeys.value = value
+    }
 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index c2cc29961e..b3967d294b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -64,6 +64,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 
     override var themeId: Int = 0
 
+    private val CHECKED_DECRYPTION = "CheckedDecryption"
+    private var checkedDecryption = false
+
     override fun onCreate(savedInstanceState: Bundle?) {
         val splashScreen = installSplashScreen()
         splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
@@ -75,6 +78,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
         binding = ActivityMainBinding.inflate(layoutInflater)
         setContentView(binding.root)
 
+        if (savedInstanceState != null) {
+            checkedDecryption = savedInstanceState.getBoolean(CHECKED_DECRYPTION)
+        }
+        if (!checkedDecryption) {
+            val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext)
+                .getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true)
+            if (!firstTimeSetup) {
+                checkKeys()
+            }
+            checkedDecryption = true
+        }
+
         WindowCompat.setDecorFitsSystemWindows(window, false)
         window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
 
@@ -150,6 +165,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
                     }
                 }
             }
+            launch {
+                repeatOnLifecycle(Lifecycle.State.CREATED) {
+                    homeViewModel.checkKeys.collect {
+                        if (it) {
+                            checkKeys()
+                            homeViewModel.setCheckKeys(false)
+                        }
+                    }
+                }
+            }
         }
 
         // Dismiss previous notifications (should not happen unless a crash occurred)
@@ -158,6 +183,21 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
         setInsets()
     }
 
+    private fun checkKeys() {
+        if (!NativeLibrary.areKeysPresent()) {
+            MessageDialogFragment.newInstance(
+                titleId = R.string.keys_missing,
+                descriptionId = R.string.keys_missing_description,
+                helpLinkId = R.string.keys_missing_help
+            ).show(supportFragmentManager, MessageDialogFragment.TAG)
+        }
+    }
+
+    override fun onSaveInstanceState(outState: Bundle) {
+        super.onSaveInstanceState(outState)
+        outState.putBoolean(CHECKED_DECRYPTION, checkedDecryption)
+    }
+
     fun finishSetup(navController: NavController) {
         navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment)
         (binding.navigationView as NavigationBarView).setupWithNavController(navController)
@@ -349,6 +389,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
                     R.string.install_keys_success,
                     Toast.LENGTH_SHORT
                 ).show()
+                homeViewModel.setCheckKeys(true)
                 gamesViewModel.reloadGames(true)
                 return true
             } else {
@@ -399,6 +440,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
                         firmwarePath.deleteRecursively()
                         cacheFirmwareDir.copyRecursively(firmwarePath, true)
                         NativeLibrary.initializeSystem(true)
+                        homeViewModel.setCheckKeys(true)
                         getString(R.string.save_file_imported_success)
                     }
                 } catch (e: Exception) {
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 4c3644cc58..e51453ecaa 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -913,4 +913,10 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env,
     EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries();
 }
 
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_areKeysPresent(JNIEnv* env, jobject jobj) {
+    auto& system = EmulationSession::GetInstance().System();
+    system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
+    return ContentManager::AreKeysPresent();
+}
+
 } // extern "C"
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 779eb36a83..3cd1586fd4 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -144,6 +144,9 @@
     <string name="no_save_data_found">No save data found</string>
     <string name="verify_installed_content">Verify installed content</string>
     <string name="verify_installed_content_description">Checks all installed content for corruption</string>
+    <string name="keys_missing">Encryption keys are missing</string>
+    <string name="keys_missing_description">Firmware and retail games cannot be decrypted</string>
+    <string name="keys_missing_help">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
 
     <!-- Applet launcher strings -->
     <string name="applets">Applet launcher</string>