From da5aa70fc940c3f0feec3bf83acab233c84966d7 Mon Sep 17 00:00:00 2001
From: Charles Lombardo <clombardo169@gmail.com>
Date: Sat, 17 Feb 2024 23:10:10 -0500
Subject: [PATCH] android: Port yuzu system info logging (#7431)

---
 .../org/citra/citra_emu/CitraApplication.kt   |  14 +++
 .../java/org/citra/citra_emu/NativeLibrary.kt |   4 +-
 .../utils/DirectoryInitialization.kt          |   6 +-
 .../java/org/citra/citra_emu/utils/Log.kt     |  27 +----
 .../org/citra/citra_emu/utils/MemoryUtil.kt   | 108 ++++++++++++++++++
 src/android/app/src/main/jni/CMakeLists.txt   |   1 +
 src/android/app/src/main/jni/native_log.cpp   |  30 +++++
 .../app/src/main/res/values/strings.xml       |  11 ++
 8 files changed, 174 insertions(+), 27 deletions(-)
 create mode 100644 src/android/app/src/main/java/org/citra/citra_emu/utils/MemoryUtil.kt
 create mode 100644 src/android/app/src/main/jni/native_log.cpp

diff --git a/src/android/app/src/main/java/org/citra/citra_emu/CitraApplication.kt b/src/android/app/src/main/java/org/citra/citra_emu/CitraApplication.kt
index c414d4246..75a88baf1 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/CitraApplication.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/CitraApplication.kt
@@ -9,10 +9,13 @@ import android.app.Application
 import android.app.NotificationChannel
 import android.app.NotificationManager
 import android.content.Context
+import android.os.Build
 import org.citra.citra_emu.utils.DirectoryInitialization
 import org.citra.citra_emu.utils.DocumentsTree
 import org.citra.citra_emu.utils.GpuDriverHelper
 import org.citra.citra_emu.utils.PermissionsHandler
+import org.citra.citra_emu.utils.Log
+import org.citra.citra_emu.utils.MemoryUtil
 
 class CitraApplication : Application() {
     private fun createNotificationChannel() {
@@ -53,9 +56,20 @@ class CitraApplication : Application() {
         }
 
         NativeLibrary.logDeviceInfo()
+        logDeviceInfo()
         createNotificationChannel()
     }
 
+    fun logDeviceInfo() {
+        Log.info("Device Manufacturer - ${Build.MANUFACTURER}")
+        Log.info("Device Model - ${Build.MODEL}")
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
+            Log.info("SoC Manufacturer - ${Build.SOC_MANUFACTURER}")
+            Log.info("SoC Model - ${Build.SOC_MODEL}")
+        }
+        Log.info("Total System Memory - ${MemoryUtil.getDeviceRAM()}")
+    }
+
     companion object {
         private var application: CitraApplication? = null
 
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt
index 75150fd16..bfbe658f8 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt
@@ -413,12 +413,12 @@ object NativeLibrary {
     }
 
     fun setEmulationActivity(emulationActivity: EmulationActivity?) {
-        Log.verbose("[NativeLibrary] Registering EmulationActivity.")
+        Log.debug("[NativeLibrary] Registering EmulationActivity.")
         sEmulationActivity = WeakReference(emulationActivity)
     }
 
     fun clearEmulationActivity() {
-        Log.verbose("[NativeLibrary] Unregistering EmulationActivity.")
+        Log.debug("[NativeLibrary] Unregistering EmulationActivity.")
         sEmulationActivity.clear()
     }
 
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/citra/citra_emu/utils/DirectoryInitialization.kt
index 10e509f23..c48f9d22e 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/utils/DirectoryInitialization.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/DirectoryInitialization.kt
@@ -94,14 +94,14 @@ object DirectoryInitialization {
         val dataPath = PermissionsHandler.citraDirectory
         if (dataPath.toString().isNotEmpty()) {
             userPath = dataPath.toString()
-            Log.debug("[DirectoryInitialization] User Dir: $userPath")
+            android.util.Log.d("[Citra Frontend]", "[DirectoryInitialization] User Dir: $userPath")
             return true
         }
         return false
     }
 
     private fun copyAsset(asset: String, output: File, overwrite: Boolean, context: Context) {
-        Log.verbose("[DirectoryInitialization] Copying File $asset to $output")
+        Log.debug("[DirectoryInitialization] Copying File $asset to $output")
         try {
             if (!output.exists() || overwrite) {
                 val inputStream = context.assets.open(asset)
@@ -121,7 +121,7 @@ object DirectoryInitialization {
         overwrite: Boolean,
         context: Context
     ) {
-        Log.verbose("[DirectoryInitialization] Copying Folder $assetFolder to $outputFolder")
+        Log.debug("[DirectoryInitialization] Copying Folder $assetFolder to $outputFolder")
         try {
             var createdFolder = false
             for (file in context.assets.list(assetFolder)!!) {
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/Log.kt b/src/android/app/src/main/java/org/citra/citra_emu/utils/Log.kt
index 26c41bc98..f691d51b0 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/utils/Log.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/Log.kt
@@ -4,34 +4,17 @@
 
 package org.citra.citra_emu.utils
 
-import android.util.Log
-import org.citra.citra_emu.BuildConfig
-
-/**
- * Contains methods that call through to [android.util.Log], but
- * with the same TAG automatically provided. Also no-ops VERBOSE and DEBUG log
- * levels in release builds.
- */
 object Log {
     // Tracks whether we should share the old log or the current log
     var gameLaunched = false
-    private const val TAG = "Citra Frontend"
 
-    fun verbose(message: String?) {
-        if (BuildConfig.DEBUG) {
-            Log.v(TAG, message!!)
-        }
-    }
+    external fun debug(message: String)
 
-    fun debug(message: String?) {
-        if (BuildConfig.DEBUG) {
-            Log.d(TAG, message!!)
-        }
-    }
+    external fun warning(message: String)
 
-    fun info(message: String?) = Log.i(TAG, message!!)
+    external fun info(message: String)
 
-    fun warning(message: String?) = Log.w(TAG, message!!)
+    external fun error(message: String)
 
-    fun error(message: String?) = Log.e(TAG, message!!)
+    external fun critical(message: String)
 }
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/citra/citra_emu/utils/MemoryUtil.kt
new file mode 100644
index 000000000..4bf1d88c7
--- /dev/null
+++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/MemoryUtil.kt
@@ -0,0 +1,108 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.citra.citra_emu.utils
+
+import android.app.ActivityManager
+import android.content.Context
+import android.os.Build
+import org.citra.citra_emu.CitraApplication
+import org.citra.citra_emu.R
+import java.util.Locale
+import kotlin.math.ceil
+
+object MemoryUtil {
+    private val context get() = CitraApplication.appContext
+
+    private val Float.hundredths: String
+        get() = String.format(Locale.ROOT, "%.2f", this)
+
+    const val Kb: Float = 1024F
+    const val Mb = Kb * 1024
+    const val Gb = Mb * 1024
+    const val Tb = Gb * 1024
+    const val Pb = Tb * 1024
+    const val Eb = Pb * 1024
+
+    fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String =
+        when {
+            size < Kb -> {
+                context.getString(
+                    R.string.memory_formatted,
+                    size.hundredths,
+                    context.getString(R.string.memory_byte_shorthand)
+                )
+            }
+            size < Mb -> {
+                context.getString(
+                    R.string.memory_formatted,
+                    if (roundUp) ceil(size / Kb) else (size / Kb).hundredths,
+                    context.getString(R.string.memory_kilobyte)
+                )
+            }
+            size < Gb -> {
+                context.getString(
+                    R.string.memory_formatted,
+                    if (roundUp) ceil(size / Mb) else (size / Mb).hundredths,
+                    context.getString(R.string.memory_megabyte)
+                )
+            }
+            size < Tb -> {
+                context.getString(
+                    R.string.memory_formatted,
+                    if (roundUp) ceil(size / Gb) else (size / Gb).hundredths,
+                    context.getString(R.string.memory_gigabyte)
+                )
+            }
+            size < Pb -> {
+                context.getString(
+                    R.string.memory_formatted,
+                    if (roundUp) ceil(size / Tb) else (size / Tb).hundredths,
+                    context.getString(R.string.memory_terabyte)
+                )
+            }
+            size < Eb -> {
+                context.getString(
+                    R.string.memory_formatted,
+                    if (roundUp) ceil(size / Pb) else (size / Pb).hundredths,
+                    context.getString(R.string.memory_petabyte)
+                )
+            }
+            else -> {
+                context.getString(
+                    R.string.memory_formatted,
+                    if (roundUp) ceil(size / Eb) else (size / Eb).hundredths,
+                    context.getString(R.string.memory_exabyte)
+                )
+            }
+        }
+
+    val totalMemory: Float
+        get() {
+            val memInfo = ActivityManager.MemoryInfo()
+            with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
+                getMemoryInfo(memInfo)
+            }
+
+            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+                memInfo.advertisedMem.toFloat()
+            } else {
+                memInfo.totalMem.toFloat()
+            }
+        }
+
+    fun isLessThan(minimum: Int, size: Float): Boolean =
+        when (size) {
+            Kb -> totalMemory < Mb && totalMemory < minimum
+            Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum
+            Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum
+            Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum
+            Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum
+            Eb -> totalMemory / Eb < minimum
+            else -> totalMemory < Kb && totalMemory < minimum
+        }
+
+    // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for
+    // the potential error created by memInfo.totalMem
+    fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory, true)
+}
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index 84a34441a..233d568ed 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -28,6 +28,7 @@ add_library(citra-android SHARED
     ndk_motion.cpp
     ndk_motion.h
     system_save_game.cpp
+    native_log.cpp
 )
 
 target_link_libraries(citra-android PRIVATE audio_core citra_common citra_core input_common network)
diff --git a/src/android/app/src/main/jni/native_log.cpp b/src/android/app/src/main/jni/native_log.cpp
new file mode 100644
index 000000000..86a57b99f
--- /dev/null
+++ b/src/android/app/src/main/jni/native_log.cpp
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <common/logging/log.h>
+#include <jni.h>
+#include "android_common/android_common.h"
+
+extern "C" {
+
+void Java_org_citra_citra_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) {
+    LOG_DEBUG(Frontend, "{}", GetJString(env, jmessage));
+}
+
+void Java_org_citra_citra_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) {
+    LOG_WARNING(Frontend, "{}", GetJString(env, jmessage));
+}
+
+void Java_org_citra_citra_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) {
+    LOG_INFO(Frontend, "{}", GetJString(env, jmessage));
+}
+
+void Java_org_citra_citra_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) {
+    LOG_ERROR(Frontend, "{}", GetJString(env, jmessage));
+}
+
+void Java_org_citra_citra_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) {
+    LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage));
+}
+
+} // 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 303632162..781e78da1 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -442,6 +442,17 @@
     <string name="cia_install_error_encrypted">\"%s\" must be decrypted before being used with Citra.\n A real 3DS is required</string>
     <string name="cia_install_error_unknown">An unknown error occurred while installing \"%s\".\n Please see the log for more details</string>
 
+    <!-- Memory Sizes -->
+    <string name="memory_formatted">%1$s %2$s</string>
+    <string name="memory_byte">Byte</string>
+    <string name="memory_byte_shorthand">B</string>
+    <string name="memory_kilobyte">KB</string>
+    <string name="memory_megabyte">MB</string>
+    <string name="memory_gigabyte">GB</string>
+    <string name="memory_terabyte">TB</string>
+    <string name="memory_petabyte">PB</string>
+    <string name="memory_exabyte">EB</string>
+
     <!-- Theme Modes -->
     <string name="change_theme_mode">Change Theme Mode</string>
     <string name="theme_mode_follow_system">Follow System</string>