From 437535837ee1aeea8302a2ec60a28db4752f2ca2 Mon Sep 17 00:00:00 2001 From: chenfufeng Date: Thu, 25 Aug 2022 17:04:40 +0800 Subject: [PATCH] =?UTF-8?q?[Feat]=E6=96=B0=E5=A2=9E=E6=80=A7=E8=83=BD?= =?UTF-8?q?=E7=9B=91=E6=8E=A7=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 3 + app/src/main/AndroidManifest.xml | 9 + .../com/mogo/launcher/MogoApplication.java | 5 - config.gradle | 6 +- .../mogo-core-function-devatools/build.gradle | 1 - .../DevaToolsProvider.kt | 10 + .../monitor/CpuMonitor.kt | 109 +++++++ .../monitor/MemMonitor.kt | 121 +++++++ .../monitor/MonitorManager.kt | 64 ++++ .../monitor/db/CpuInfo.kt | 18 ++ .../monitor/db/MemInfo.kt | 27 ++ .../monitor/db/MonitorDao.kt | 24 ++ .../monitor/db/MonitorDb.kt | 27 ++ .../monitor/remote/IUserInterface.java | 17 + .../monitor/remote/RemoteUserServiceImp.java | 141 ++++++++ .../monitor/remote/UserServiceManager.kt | 182 +++++++++++ .../monitor/utils/CpuUtils.java | 298 +++++++++++++++++ .../monitor/utils/DoubleUtils.kt | 44 +++ .../monitor/utils/FileUtil.kt | 304 ++++++++++++++++++ .../monitor/utils/HookUtils.java | 54 ++++ .../monitor/utils/MemUtils.java | 180 +++++++++++ .../core/function/hmi/ui/MoGoHmiFragment.kt | 15 +- .../function/main/MainMoGoApplication.java | 17 + .../api/devatools/IDevaToolsProvider.kt | 4 + .../call/devatools/CallerDevaToolsManager.kt | 8 + 25 files changed, 1678 insertions(+), 10 deletions(-) create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/CpuMonitor.kt create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/MemMonitor.kt create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/MonitorManager.kt create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/CpuInfo.kt create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/MemInfo.kt create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/MonitorDao.kt create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/MonitorDb.kt create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/remote/IUserInterface.java create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/remote/RemoteUserServiceImp.java create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/remote/UserServiceManager.kt create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/CpuUtils.java create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/DoubleUtils.kt create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/FileUtil.kt create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/HookUtils.java create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/MemUtils.java diff --git a/app/build.gradle b/app/build.gradle index ead027397f..ba5a04b45f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -243,6 +243,9 @@ dependencies { debugImplementation rootProject.ext.dependencies.debugleakcanary releaseImplementation rootProject.ext.dependencies.releaseleakcanary implementation rootProject.ext.dependencies.android_start_up + +// // 暂不使用Shizuku-API +// implementation rootProject.ext.dependencies.shizuku_provider if (Boolean.valueOf(USE_MAVEN_PACKAGE)) { implementation rootProject.ext.dependencies.ttspad implementation rootProject.ext.dependencies.mogo_core_function_hmi diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6b757d0af8..f4b753b2c2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -31,6 +31,15 @@ android:value="android.startup.provider.config" /> + + + + + + + + + () + } + + private val dateFormat by lazy { + SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + } + + companion object { + private const val TAG = "CpuMonitor" + const val INTERVAL_PERF = 1000L + + @SuppressLint("StaticFieldLeak") + @Volatile + private var sInstance: CpuMonitor? = null + fun getInstance(context: Context): CpuMonitor? { + if (sInstance == null) { + synchronized(CpuMonitor::class.java) { + if (sInstance == null) { + sInstance = CpuMonitor(context) + } + } + } + return sInstance + } + } + + /** + * 请先运行Shizuku的服务 + */ + @Synchronized + fun startRun() { + if (!isRunning) { + Thread(CpuRunnable()).start() + isRunning = true + } + } + + @Synchronized + fun isStarted() = isRunning + + @Synchronized + fun stop() { + isRunning = false + } + + fun saveCpuInfos() { + synchronized(this) { + MonitorDb.getDb(context).monitorDao().saveAllCpuInfos(*cpuInfoList.toTypedArray()) + cpuInfoList.clear() + } + } + + private fun getCpuValue(): Double { + var value = 0.0 + try { + // 应用获取当前进程CPU使用时间,Binder服务端获取系统CPU时间,中间用耗时,不如使用top命令 +// value = UserServiceManager.getCpuUsage(Process.myPid()) + value = CpuUtils.getCpuUsageForO() + return value + } catch (e: Exception) { + Log.e(TAG, e.message ?: "未知异常!") + } + return value + } + + inner class CpuRunnable : Runnable { + override fun run() { + while (isRunning) { + val cpu = getCpuValue() + if (cpu > 0) { + synchronized(this@CpuMonitor) { + cpuInfoList.add( + CpuInfo( + saveTime = dateFormat.format(System.currentTimeMillis()), + cpuPercent = cpu + ) + ) + } + } + try { + Thread.sleep(INTERVAL_PERF) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + } +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/MemMonitor.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/MemMonitor.kt new file mode 100644 index 0000000000..d712cfc73b --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/MemMonitor.kt @@ -0,0 +1,121 @@ +package com.zhjt.mogo_core_function_devatools.monitor + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Process.myPid +import android.util.Log +import com.zhjt.mogo_core_function_devatools.monitor.db.MemInfo +import com.zhjt.mogo_core_function_devatools.monitor.db.MonitorDb +import com.zhjt.mogo_core_function_devatools.monitor.utils.HookUtils +import com.zhjt.mogo_core_function_devatools.monitor.utils.MemUtils +import java.text.SimpleDateFormat + +class MemMonitor private constructor(var context: Context) { + + var isRunning = false + + private val memInfoList by lazy { + ArrayList() + } + + private val dateFormat by lazy { + SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + } + + companion object { + private const val TAG = "MemMonitor" + const val INTERVAL_PERF = 1000L + + @SuppressLint("StaticFieldLeak") + @Volatile + private var sInstance: MemMonitor? = null + fun getInstance(context: Context): MemMonitor? { + if (sInstance == null) { + synchronized(MemMonitor::class.java) { + if (sInstance == null) { + sInstance = MemMonitor(context) + } + } + } + return sInstance + } + } + + @Synchronized + fun startRun() { + if (!isRunning) { + Thread(MemoryRunnable()).start() + isRunning = true + } + } + + @Synchronized + fun isStarted() = isRunning + + @Synchronized + fun stop() { + isRunning = false + } + + fun saveMemInfos() { + synchronized(this) { + MonitorDb.getDb(context).monitorDao().saveAllMemInfos(*memInfoList.toTypedArray()) + memInfoList.clear() + } + } + + /** + * 单位MB + */ + private fun getMemoryValue(): Double { + var value = 0.0 + try { + value = MemUtils.getPssMemValue(context, intArrayOf(myPid())) + return value + } catch (e: Exception) { + Log.e(TAG, e.message ?: "未知异常!") + } + return value + } + + /** + * 单位MB + */ + private fun getMemoryArray(): DoubleArray { + var value = doubleArrayOf() + try { + value = MemUtils.getPssArray(context, intArrayOf(myPid())) + return value + } catch (e: Exception) { + Log.e(TAG, e.message ?: "未知异常!") + } + return value + } + + inner class MemoryRunnable : Runnable { + override fun run() { + while (isRunning) { + // 0: 总PSS,1: dalvik总Pss, 2: native总Pss, 3: other总Pss + val pssArray = getMemoryArray() + if (pssArray[0] > 0) { + synchronized(this@MemMonitor) { + memInfoList.add( + MemInfo( + saveTime = dateFormat.format(System.currentTimeMillis()), + totalPss = pssArray[0], + dalvikPss = pssArray[1], + nativePss = pssArray[2], + otherPss = pssArray[3] + ) + ) + } + } + try { + Thread.sleep(INTERVAL_PERF) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + } +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/MonitorManager.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/MonitorManager.kt new file mode 100644 index 0000000000..6e8564140a --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/MonitorManager.kt @@ -0,0 +1,64 @@ +package com.zhjt.mogo_core_function_devatools.monitor + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Looper +import com.mogo.eagle.core.utilcode.util.ToastUtils +import java.util.* + +class MonitorManager private constructor(var context: Context) { + + private var timer: Timer? = null + + private var isStarted = false + + companion object { + @SuppressLint("StaticFieldLeak") + @Volatile + private var sInstance: MonitorManager? = null + fun getInstance(context: Context): MonitorManager? { + if (sInstance == null) { + synchronized(MonitorManager::class.java) { + if (sInstance == null) { + sInstance = MonitorManager(context) + } + } + } + return sInstance + } + } + + /** + * 主线程中执行 + */ + fun startMonitor() { + if (Thread.currentThread() == Looper.getMainLooper().thread) { + if (!isStarted) { + CpuMonitor.getInstance(context)?.startRun() + MemMonitor.getInstance(context)?.startRun() + if (timer == null) { + timer = Timer() + } + timer!!.schedule(object : TimerTask() { + override fun run() { + CpuMonitor.getInstance(context)?.saveCpuInfos() + MemMonitor.getInstance(context)?.saveMemInfos() + } + }, 1000, 60000) + isStarted = true + ToastUtils.showShort("性能监控启动成功!") + } else { + ToastUtils.showShort("性能监控已启动,请勿重复启动!") + } + } + } + + fun stopMonitor() { + CpuMonitor.getInstance(context)?.stop() + MemMonitor.getInstance(context)?.stop() + timer?.cancel() + timer = null + isStarted = false + ToastUtils.showShort("性能监控停止成功!") + } +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/CpuInfo.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/CpuInfo.kt new file mode 100644 index 0000000000..8a1c0aac96 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/CpuInfo.kt @@ -0,0 +1,18 @@ +package com.zhjt.mogo_core_function_devatools.monitor.db + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "t_cpu") +data class CpuInfo( + @PrimaryKey(autoGenerate = true) + @ColumnInfo(name = "id") + val uuid: Long = 0, + + @ColumnInfo(name = "save_time") + val saveTime: String?, + + @ColumnInfo(name = "cpu_percent") + val cpuPercent: Double +) \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/MemInfo.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/MemInfo.kt new file mode 100644 index 0000000000..4861c56c47 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/MemInfo.kt @@ -0,0 +1,27 @@ +package com.zhjt.mogo_core_function_devatools.monitor.db + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "t_memory") +data class MemInfo( + @PrimaryKey(autoGenerate = true) + @ColumnInfo(name = "id") + val uuid: Long = 0, + + @ColumnInfo(name = "save_time") + val saveTime: String, + + @ColumnInfo(name = "total_pss") + val totalPss: Double, + + @ColumnInfo(name = "dalvik_pss") + val dalvikPss: Double, + + @ColumnInfo(name = "native_pss") + val nativePss: Double, + + @ColumnInfo(name = "other_pss") + val otherPss: Double +) \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/MonitorDao.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/MonitorDao.kt new file mode 100644 index 0000000000..6eed72b029 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/MonitorDao.kt @@ -0,0 +1,24 @@ +package com.zhjt.mogo_core_function_devatools.monitor.db + +import androidx.room.* + +@Dao +interface MonitorDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun saveCpu(info: CpuInfo) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun saveAllCpuInfos(vararg cpuInfo: CpuInfo) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun saveMemory(info: MemInfo) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun saveAllMemInfos(vararg memInfo: MemInfo) + + @Query("SELECT * FROM t_cpu WHERE id =:id") + fun getAllCPUById(id: Long): List + + @Query("SELECT * FROM t_memory WHERE id =:id") + fun getAllMemById(id: Long): List +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/MonitorDb.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/MonitorDb.kt new file mode 100644 index 0000000000..600198148a --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/db/MonitorDb.kt @@ -0,0 +1,27 @@ +package com.zhjt.mogo_core_function_devatools.monitor.db + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase + +@Database(entities = [CpuInfo::class, MemInfo::class], version = 1, exportSchema = false) +abstract class MonitorDb: RoomDatabase() { + abstract fun monitorDao(): MonitorDao + + companion object { + const val INTERNAL_DB_NAME = "mogo_monitor.db" + + private var db: MonitorDb? = null + + @JvmStatic + fun getDb(context: Context): MonitorDb { + if (db == null) { + db = Room.databaseBuilder(context.applicationContext, MonitorDb::class.java, INTERNAL_DB_NAME) + .fallbackToDestructiveMigration() + .build() + } + return db!! + } + } +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/remote/IUserInterface.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/remote/IUserInterface.java new file mode 100644 index 0000000000..488228b6a2 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/remote/IUserInterface.java @@ -0,0 +1,17 @@ +package com.zhjt.mogo_core_function_devatools.monitor.remote; + +import android.os.IBinder; +import android.os.IInterface; +import android.os.RemoteException; + +public interface IUserInterface extends IInterface { + + String DESCRIPTOR = "rikka.shizuku.demo.IUserInterface"; + + int TRANSACTION_exec = IBinder.FIRST_CALL_TRANSACTION; + int TRANSACTION_getCpuUsage = IBinder.FIRST_CALL_TRANSACTION + 1; + + String exec(String cmd) throws RemoteException; + + double getCpuUsage(int pid) throws RemoteException; +} diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/remote/RemoteUserServiceImp.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/remote/RemoteUserServiceImp.java new file mode 100644 index 0000000000..d9f1d3487a --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/remote/RemoteUserServiceImp.java @@ -0,0 +1,141 @@ +package com.zhjt.mogo_core_function_devatools.monitor.remote; + +import android.os.Binder; +import android.os.IBinder; +import android.os.IInterface; +import android.os.Parcel; +import android.os.RemoteException; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.zhjt.mogo_core_function_devatools.monitor.utils.CpuUtils; + +/** + * Server端Binder类 + */ +public class RemoteUserServiceImp extends Binder implements IUserInterface { + + public RemoteUserServiceImp() { + attachInterface(this, DESCRIPTOR); + } + + public static IUserInterface asInterface(IBinder obj) { + if ((obj == null)) { + return null; + } + IInterface iin = obj.queryLocalInterface(DESCRIPTOR); + if (iin instanceof IUserInterface) { + return ((IUserInterface) iin); + } + return new Proxy(obj); + } + + @Override + public String exec(String cmd) throws RemoteException { + return ""; + } + + @Override + public double getCpuUsage(int pid) throws RemoteException { + return CpuUtils.getCpuUsagePercentWithPid(pid); + } + + /** + * 处理Client端请求 + * + * @param code + * @param data + * @param reply + * @param flags + * @return + * @throws RemoteException + */ + @Override + protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException { + final String descriptor = DESCRIPTOR; + switch (code) { + case TRANSACTION_exec: + data.enforceInterface(descriptor); + String _arg0; + _arg0 = data.readString(); + String _result = this.exec(_arg0); + reply.writeNoException(); + reply.writeString(_result); + return true; + default: + return super.onTransact(code, data, reply, flags); + } + } + + @Nullable + @Override + public String getInterfaceDescriptor() { + return super.getInterfaceDescriptor(); + } + + /** + * 返回给Client端并被封装成BinderProxy + * + * @return + */ + @Override + public IBinder asBinder() { + return this; + } + + public static class Proxy implements IUserInterface { + private IBinder mRemote; + + public Proxy(IBinder remote) { + mRemote = remote; + } + + public String getInterfaceDescriptor() { + return DESCRIPTOR; + } + + @Override + public IBinder asBinder() { + // 返回Server端Binder对象 + return mRemote; + } + + @Override + public String exec(String cmd) throws RemoteException { + // 参数传递给Server端并挂起当前线程,等待Server端执行完返回结果 + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + String _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(cmd); + mRemote.transact(TRANSACTION_exec, _data, _reply, 0); + _reply.readException(); + _result = _reply.readString(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } + + @Override + public double getCpuUsage(int pid) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + double _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeInt(pid); + mRemote.transact(TRANSACTION_getCpuUsage, _data, _reply, 0); + _reply.readException(); + _result = _reply.readDouble(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } + } +} diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/remote/UserServiceManager.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/remote/UserServiceManager.kt new file mode 100644 index 0000000000..7eaf674df2 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/remote/UserServiceManager.kt @@ -0,0 +1,182 @@ +package com.zhjt.mogo_core_function_devatools.monitor.remote + +import rikka.shizuku.Shizuku +import android.content.ServiceConnection +import android.content.ComponentName +import android.os.IBinder +import rikka.shizuku.Shizuku.UserServiceArgs +import android.content.pm.PackageManager +import android.util.Log +import com.mogo.eagle.core.utilcode.util.AppUtils +import com.mogo.eagle.core.utilcode.util.ToastUtils +import rikka.shizuku.Shizuku.OnBinderReceivedListener +import rikka.shizuku.Shizuku.OnBinderDeadListener +import rikka.shizuku.Shizuku.OnRequestPermissionResultListener +import java.lang.StringBuilder + +object UserServiceManager { + + private const val TAG = "UserServiceManager" + private const val REQUEST_CODE_BIND = 1 + private const val REQUEST_CODE_UNBIND = 2 + + private var userServiceProxy: IUserInterface? = null + private var isFirst = true + + fun init() { + if (isFirst) { + addListener() + isFirst = false + } + bindUserService() + } + + fun stopService() { + removeListener() + unbindUserService() + } + + fun isServiceActive(): Boolean { + var isActive = false + val service = userServiceProxy + if (service != null && service is IBinder && service.isBinderAlive) { + isActive = true + } + return isActive + } + + private fun addListener() { + Shizuku.addBinderReceivedListenerSticky(BINDER_RECEIVED_LISTENER) + Shizuku.addBinderDeadListener(BINDER_DEAD_LISTENER) + Shizuku.addRequestPermissionResultListener(REQUEST_PERMISSION_RESULT_LISTENER) + } + + private fun removeListener() { + Shizuku.removeBinderReceivedListener(BINDER_RECEIVED_LISTENER) + Shizuku.removeBinderDeadListener(BINDER_DEAD_LISTENER) + Shizuku.removeRequestPermissionResultListener(REQUEST_PERMISSION_RESULT_LISTENER) + } + + private fun bindUserService() { + if (checkPermission(REQUEST_CODE_BIND)) { + val res = StringBuilder() + try { + if (Shizuku.getVersion() < 10) { + res.append("requires Shizuku API 10") + } else { + Shizuku.bindUserService(userServiceArgs, userServiceConnection) + } + } catch (tr: Throwable) { + tr.printStackTrace() + res.append(tr.toString()) + } + } + } + + private fun unbindUserService() { + if (checkPermission(REQUEST_CODE_UNBIND)) { + val res = StringBuilder() + try { + if (Shizuku.getVersion() < 10) { + res.append("requires Shizuku API 10") + } else { + Shizuku.unbindUserService(userServiceArgs, userServiceConnection, true) + } + } catch (tr: Throwable) { + tr.printStackTrace() + res.append(tr.toString()) + } + } + } + + fun exec(cmd: String): String { + return userServiceProxy?.exec(cmd) ?: "" + } + + /** + * 单位:%,保留1位小数 + */ + fun getCpuUsage(pid: Int): Double { + return userServiceProxy?.getCpuUsage(pid) ?: -1.0 + } + + private val userServiceConnection: ServiceConnection = object : ServiceConnection { + override fun onServiceConnected(componentName: ComponentName, binder: IBinder?) { + if (binder != null && binder.pingBinder()) { + val service = RemoteUserServiceImp.asInterface(binder) + userServiceProxy = service + ToastUtils.showShort("监控权限获取成功!") + } + } + + override fun onServiceDisconnected(componentName: ComponentName) {} + } + + private val userServiceArgs by lazy { + val appInfo = AppUtils.getAppInfo() + UserServiceArgs( + ComponentName( + appInfo?.packageName ?: "com.mogo.launcher.f", + RemoteUserServiceImp::class.java.name + ) + ) + .daemon(false) + .processNameSuffix("service") + .debuggable(true) + .version(appInfo?.versionCode ?: 1) + } + + private fun checkPermission(code: Int): Boolean { + if (Shizuku.isPreV11()) { + Log.w(TAG, "Version is preV11!") + return false + } + try { + return if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) { + true + } else if (Shizuku.shouldShowRequestPermissionRationale()) { + Log.e(TAG, "User denied permission (shouldShowRequestPermissionRationale=true)") + false + } else { + Shizuku.requestPermission(code) + false + } + } catch (e: Throwable) { + Log.e(TAG, Log.getStackTraceString(e)) + ToastUtils.showLong("请先打开Shizuku并启动它!") + } + return false + } + + private fun onRequestPermissionsResult(requestCode: Int, grantResult: Int) { + if (grantResult == PackageManager.PERMISSION_GRANTED) { + when (requestCode) { + REQUEST_CODE_BIND -> { + bindUserService() + } + REQUEST_CODE_UNBIND -> { + unbindUserService() + } + else -> {} + } + } else { + Log.e(TAG, "User denied permission") + } + } + + private val BINDER_RECEIVED_LISTENER = OnBinderReceivedListener { + if (Shizuku.isPreV11()) { + Log.w(TAG, "Shizuku pre-v11 is not supported") + } else { + Log.d(TAG, "Binder received") + } + } + private val BINDER_DEAD_LISTENER = OnBinderDeadListener { Log.w(TAG, "Binder dead") } + private val REQUEST_PERMISSION_RESULT_LISTENER = + OnRequestPermissionResultListener { requestCode: Int, grantResult: Int -> + onRequestPermissionsResult( + requestCode, + grantResult + ) + } +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/CpuUtils.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/CpuUtils.java new file mode 100644 index 0000000000..394c77892c --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/CpuUtils.java @@ -0,0 +1,298 @@ +package com.zhjt.mogo_core_function_devatools.monitor.utils; + +import android.os.Process; +import android.text.TextUtils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.RandomAccessFile; + +/** + * CPU相关工具类。 + */ +public class CpuUtils { + private static boolean initCpu = true; + private static double oCpu = 0.0; + private static double oIdle = 0.0; + + private static double pJif = 0.0; + private static double pCpu = 0.0; + private static double aCpu = 0.0; + private static double opCpu = 0.0; + private static double oaCpu = 0.0; + + /** + * 8.0以上获取cpu的方式 + * + * @return + */ + public static double getCpuUsageForO() { + java.lang.Process process = null; + try { + process = Runtime.getRuntime().exec("top -n 1"); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + int cpuIndex = -1; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (TextUtils.isEmpty(line)) { + continue; + } + int tempIndex = getCPUIndex(line); + if (tempIndex != -1) { + cpuIndex = tempIndex; + continue; + } + if (line.startsWith(String.valueOf(Process.myPid()))) { + if (cpuIndex == -1) { + continue; + } + String[] param = line.split("\\s+"); + if (param.length <= cpuIndex) { + continue; + } + String cpu = param[cpuIndex]; + if (cpu.endsWith("%")) { + cpu = cpu.substring(0, cpu.lastIndexOf("%")); + } + return Double.parseDouble(cpu) / Runtime.getRuntime().availableProcessors(); + } + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (process != null) { + process.destroy(); + } + } + return 0; + } + + private static int getCPUIndex(String line) { + if (line.contains("CPU")) { + String[] titles = line.split("\\s+"); + for (int i = 0; i < titles.length; i++) { + if (titles[i].contains("CPU")) { + return i; + } + } + } + return -1; + } + + /** + * 获取应用的CPU占用率,单位:%,保留1位小数 + * @param pid + * @return + */ + public static double getCpuUsagePercentWithPid(int pid) { + double usage = 0.0; + String[] result1; + String[] result2; + if (pid >= 0) { + result1 = getCpuWithPid(pid); + if (null != result1) { + pCpu = Double.parseDouble(result1[1]) + + Double.parseDouble(result1[2]); + } + result2 = getSystemCpu(); + if (null != result2) { + aCpu = 0.0; + for (int i = 2; i < result2.length; i++) { + if (result2[i] != null) { + aCpu += Double.parseDouble(result2[i]); + } + } + } + if ((aCpu - oaCpu) != 0) { + usage = DoubleUtils.div(((pCpu - opCpu) * 100.00), + (aCpu - oaCpu), 1); + if (usage < 0) { + usage = 0; + } + else if (usage > 100) + { + usage = 100; + } + + } + opCpu = pCpu; + oaCpu = aCpu; + } + pJif = pCpu; + return usage; + } + + /** + * 获取CPU使用率 + * + * @return CPU使用率,单位:% + */ + public static double getSysCpuUsagePercent() { + double usage = 0.0; + if (initCpu) { + initCpu = false; + RandomAccessFile reader = null; + try { + reader = new RandomAccessFile("/proc/stat", + "r"); + String load; + load = reader.readLine(); + String[] toks = load.split(" "); + oIdle = Double.parseDouble(toks[5]); + oCpu = Double.parseDouble(toks[2]) + + Double.parseDouble(toks[3]) + + Double.parseDouble(toks[4]) + + Double.parseDouble(toks[6]) + + Double.parseDouble(toks[8]) + + Double.parseDouble(toks[7]); + + } catch (IOException e) { + e.printStackTrace(); + } + finally + { + FileUtil.closeRandomAccessFile(reader); + } + } else { + RandomAccessFile reader = null; + try { + reader = new RandomAccessFile("/proc/stat", "r"); + String load; + load = reader.readLine(); + String[] toks = load.split(" "); + double cIdle = Double.parseDouble(toks[5]); + double cCpu = Double.parseDouble(toks[2]) + + Double.parseDouble(toks[3]) + + Double.parseDouble(toks[4]) + + Double.parseDouble(toks[6]) + + Double.parseDouble(toks[8]) + + Double.parseDouble(toks[7]); + if (0 != ((cCpu + cIdle) - (oCpu + oIdle))) { + usage = DoubleUtils.div((100.00 * ((cCpu - oCpu))), + ((cCpu + cIdle) - (oCpu + oIdle)), 1); + if (usage < 0) { + usage = 0; + } + else if (usage > 100) + { + usage = 100; + } + } + oCpu = cCpu; + oIdle = cIdle; + } catch (IOException e) { + e.printStackTrace(); + } finally { + FileUtil.closeRandomAccessFile(reader); + } + } + return usage; + } + + private static String[] getCpuWithPid(int pid) { + String cpuPath = "/proc/" + pid + "/stat"; + String cpu = ""; + String[] result = new String[3]; + + File f = new File(cpuPath); + // 部分进程信息无法读取 + if (!f.exists() || !f.canRead()) + { + return result; + } + + FileReader fr = null; + BufferedReader localBufferedReader = null; + + try { + fr = new FileReader(f); + localBufferedReader = new BufferedReader(fr, 8192); + cpu = localBufferedReader.readLine(); + if (null != cpu) { + String[] cpuSplit = cpu.split(" "); + result[0] = cpuSplit[1]; + result[1] = cpuSplit[13]; + result[2] = cpuSplit[14]; + } + }catch (IOException e) { + e.printStackTrace(); + } + FileUtil.closeReader(localBufferedReader); + return result; + } + + private static String[] getSystemCpu() { + String cpuPath = "/proc/stat"; + String cpu = ""; + String[] result = new String[7]; + + File f = new File(cpuPath); + if (!f.exists() || !f.canRead()) + { + return result; + } + + FileReader fr = null; + BufferedReader localBufferedReader = null; + + try { + fr = new FileReader(f); + localBufferedReader = new BufferedReader(fr, 8192); + cpu = localBufferedReader.readLine(); + if (null != cpu) { + result = cpu.split(" "); + + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + FileUtil.closeReader(localBufferedReader); + return result; + } + + public static String getCpuUsageByCmd() { + java.lang.Process process; + StringBuilder sb = new StringBuilder(); + String line; + String cmd = "dumpsys cpuinfo"; + try { + process = Runtime.getRuntime().exec( + new String[] { "sh", "-c", cmd }); + BufferedReader br = new BufferedReader(new InputStreamReader( + process.getInputStream())); + while (((line = br.readLine()) != null)) { + // 去掉空白行数据 + line = line.trim(); + if (line.equals("")) { + continue; + } + sb.append(line); + sb.append("\r\n"); + } + try { + process.waitFor(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } catch (IOException e) { + e.printStackTrace(); + } + return sb.toString(); + } + + /** + * 获取进程的CPU使用时间片 + * + * @return 进程的CPU使用时间片 + */ + public long getJif() { + return (long)pJif; + } +} diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/DoubleUtils.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/DoubleUtils.kt new file mode 100644 index 0000000000..895889d781 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/DoubleUtils.kt @@ -0,0 +1,44 @@ +package com.zhjt.mogo_core_function_devatools.monitor.utils + +import java.lang.Exception +import java.math.BigDecimal + + +object DoubleUtils { + + fun mul(d1: Double, d2: Double): Double { + val bd1 = BigDecimal(d1.toString()) + val bd2 = BigDecimal(d2.toString()) + return try { + bd1.multiply(bd2).toDouble() + } catch (e: Exception) { + e.printStackTrace() + 0.0 + } + } + + @JvmStatic + fun div(d1: Double, d2: Double, scale: Int): Double { + val bd1 = BigDecimal(d1.toString()) + val bd2 = BigDecimal(d2.toString()) + return try { + // 直接向下取整 + bd1.divide(bd2, scale, BigDecimal.ROUND_DOWN).toDouble() + } catch (e: Exception) { + e.printStackTrace() + 0.0 + } + } + + /** + * 四舍五入 + * scale:保留多少个小数位 + */ + @JvmStatic + fun keepDecimal(v: Double, scale: Int = 1): Double { + if (scale < 0) { + throw IllegalArgumentException("The scale must be a positive integer or zero") + } + return BigDecimal(v).setScale(scale, BigDecimal.ROUND_HALF_UP).toDouble() + } +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/FileUtil.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/FileUtil.kt new file mode 100644 index 0000000000..eceb12ec16 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/FileUtil.kt @@ -0,0 +1,304 @@ +package com.zhjt.mogo_core_function_devatools.monitor.utils + +import android.net.LocalSocket +import android.util.Log +import java.io.* +import java.lang.Exception +import java.net.Socket +import java.nio.channels.FileChannel + +object FileUtil { + private const val TAG = "FileUtil" + const val separator = "/" + fun isPathStringValid(path: String?): Boolean { + if (null == path || path.isEmpty()) { + return false + } + if (path.contains(":") || path.contains("*") || path.contains("?") + || path.contains("\"") || path.contains("<") + || path.contains(">") || path.contains("|") + ) { + Log.w(TAG, "filename can not contains:*:?\"<>|") + return false + } + return true + } + + fun isPath(path: String): Boolean { + return path.contains(separator) || path.contains("\\") + } + + fun getPath(path: String): String { + val la = path.lastIndexOf(separator) + return path.substring(0, la) + } + + fun convertValidFilePath(path: String, defPosfix: String): String { + var filePath = path + if (path.contains(separator) || path.contains("\\")) { + val la = filePath.lastIndexOf(".") + if (la < 0) { + filePath = path + defPosfix + } else { + val temp = filePath.substring(la) + if (temp.contains(separator) || temp.contains("\\")) { + // "."是目录名的一部分而不是后缀名的情况 + filePath = path + defPosfix + } + // else fileName = fileName + } + } else { + if (!path.contains(".")) // 没有有后缀 + { + filePath = filePath + defPosfix + } + } + return filePath + } + + fun isFileExists(file: String?): Boolean { + try { + val f = File(file) + if (!f.exists()) { + return false + } + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + + fun isFileValid(f: File): Boolean { + if (!f.exists()) { + try { + f.createNewFile() + } catch (e: IOException) { + return false + } + f.delete() + } + return true + } + + fun isFileValid(parent: File?, name: String?): Boolean { + val f = File(parent, name) + return isFileValid(f) + } + + /** + * 删除存在的文件 + * + * @param filePath + */ + fun delExistFile(filePath: String?) { + val f = File(filePath) + if (f.exists()) f.delete() + } + + /** + * 关闭bufferReader + * + * @param br + */ + @JvmStatic + fun closeReader(br: Reader?) { + if (br != null) { + try { + br.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + } + + /** + * 关闭Writer + * + * @param wr + */ + fun closeWriter(wr: Writer?) { + if (wr != null) { + try { + wr.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + } + + /** + * flush Writer + * + * @param wr + */ + fun flushWriter(wr: Writer?) { + if (wr != null) { + try { + wr.flush() + } catch (e: IOException) { + e.printStackTrace() + } + } + } + + /** + * 输入流的关闭 + * + * @param in + */ + fun closeInputStream(`in`: InputStream?) { + if (`in` != null) { + try { + `in`.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + } + + /** + * 输出流的关闭 + * + * @param out + */ + fun closeOutputStream(out: OutputStream?) { + if (out != null) { + try { + out.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + } + + /** + * 文件管道的关闭 + * + * @param chl + */ + fun closeFileChannel(chl: FileChannel?) { + if (chl != null) { + try { + chl.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + } + + /** + * RandomAccessFile的关闭 + * + * @param f RandomAccessFile对象 + */ + @JvmStatic + fun closeRandomAccessFile(f: RandomAccessFile?) { + if (f != null) { + try { + f.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + } + + /** + * Socket的关闭 + * + * @param s Socket对象 + */ + fun colseSocket(s: Socket?) { + if (s != null) { + try { + s.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + } + + /** + * LocalSocket的关闭 + * + * @param s Socket对象 + */ + fun colseLocalSocket(s: LocalSocket?) { + if (s != null) { + try { + s.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + } + + fun deleteFile(file: File) { + if (file.exists()) { // 判断文件是否存在 + if (file.isFile) { // 判断是否是文件 + file.delete() // delete()方法 你应该知道 是删除的意思; + } else if (file.isDirectory) { // 否则如果它是一个目录 + val files = file.listFiles() // 声明目录下所有的文件 files[]; + for (i in files.indices) { // 遍历目录下所有的文件 + deleteFile(files[i]) // 把每个文件 用这个方法进行迭代 + } + } + file.delete() + } else { + Log.e(TAG, "文件不存在!" + "n") + } + } + + /** + * 拷贝文件 + * + * @param s 源文件 + * @param t 目标文件 + */ + fun copyFile(s: File?, t: File) { + var fi: FileInputStream? = null + var fo: FileOutputStream? = null + var `in`: FileChannel? = null + var out: FileChannel? = null + try { + if (!t.exists()) { + t.createNewFile() + } + fi = FileInputStream(s) + fo = FileOutputStream(t) + `in` = fi.channel + out = fo.channel + // 连接两个通道,并且从in通道读取,然后写入out通道 + `in`.transferTo(0, `in`.size(), out) + } catch (e: IOException) { + e.printStackTrace() + } finally { + closeOutputStream(fo) + closeInputStream(fi) + closeFileChannel(`in`) + closeFileChannel(out) + } + } + + fun copyInputToFile(`in`: InputStream?, path: String?) { + var bis: BufferedInputStream? = null + var fos: FileOutputStream? = null + try { + val buffer = ByteArray(10 * 1024) + bis = BufferedInputStream(`in`) + fos = FileOutputStream(path) + var a = bis.read(buffer, 0, buffer.size) + while (a != -1) { + fos.write(buffer, 0, a) + fos.flush() + a = bis.read(buffer, 0, buffer.size) + } + } catch (e: Exception) { + e.printStackTrace() + } finally { + closeOutputStream(fos) + closeInputStream(bis) + closeInputStream(`in`) + } + } +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/HookUtils.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/HookUtils.java new file mode 100644 index 0000000000..bb1f53f2e4 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/HookUtils.java @@ -0,0 +1,54 @@ +package com.zhjt.mogo_core_function_devatools.monitor.utils; + +import android.os.Debug; +import android.util.Log; + +import java.lang.reflect.Field; + +public class HookUtils { + private static final String TAG = "HookUtils"; + + /** + * 单位为MB + * 0: 总PSS,1: dalvik总Pss, 2: native总Pss, 3: other总Pss + * @param memoryInfo + * @return + */ + public static double[] getPssArray(Debug.MemoryInfo memoryInfo) { + double[] pssArray = new double[4]; + if (memoryInfo != null) { + double totalPss = DoubleUtils.keepDecimal(memoryInfo.getTotalPss() / 1024.0, 3); +// Log.d(TAG, "===========Hook PSS 开始:===========\n"); +// Log.d(TAG, "总PSS内存为:" + totalPss); + pssArray[0] = totalPss; + try { + Field dalvikField = memoryInfo.getClass().getField("dalvikSwappedOutPss"); + double dalvikPss = DoubleUtils.keepDecimal(memoryInfo.dalvikPss / 1024.0, 3); + double dalvikSwappedOutPss = DoubleUtils.keepDecimal((int) dalvikField.get(memoryInfo) / 1024.0, 3); + pssArray[1] = dalvikPss + dalvikSwappedOutPss; +// Log.d(TAG, "dalvikPss is:" + dalvikSwappedOutPss); +// Log.d(TAG, "dalvikSwappedOutPss is:" + dalvikPss + "\n\n"); + + Field nativeField = memoryInfo.getClass().getField("nativeSwappedOutPss"); + double nativePss = DoubleUtils.keepDecimal(memoryInfo.nativePss / 1024.0, 3); + double nativeSwappedOutPss = DoubleUtils.keepDecimal((int) nativeField.get(memoryInfo) / 1024.0, 3); + pssArray[2] = nativePss + nativeSwappedOutPss; +// Log.d(TAG, "nativePss is:" + nativePss); +// Log.d(TAG, "nativeSwappedOutPss is:" + nativeSwappedOutPss + "\n\n"); + + Field otherField = memoryInfo.getClass().getField("otherSwappedOutPss"); + double otherPss = DoubleUtils.keepDecimal(memoryInfo.otherPss / 1024.0, 3); + double otherSwappedOutPss = DoubleUtils.keepDecimal((int) otherField.get(memoryInfo) / 1024.0, 3); + pssArray[3] = otherPss + otherSwappedOutPss; +// Log.d(TAG, "otherPss is:" + otherPss); +// Log.d(TAG, "otherSwappedOutPss is:" + otherSwappedOutPss + "\n\n"); +// +// Log.d(TAG, "===========Hook PSS 结束!===========\n"); + } catch (Exception e) { + e.printStackTrace(); + Log.e(TAG, e.getMessage()); + } + } + return pssArray; + } +} diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/MemUtils.java b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/MemUtils.java new file mode 100644 index 0000000000..438aea064e --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/monitor/utils/MemUtils.java @@ -0,0 +1,180 @@ +package com.zhjt.mogo_core_function_devatools.monitor.utils; + +import android.app.ActivityManager; +import android.content.Context; +import android.os.Debug; + +import java.lang.reflect.Method; + +/** + * 内存信息工具类。 + */ +public class MemUtils { + + public static double[] getPssArray(Context context, int[] pids) { + Debug.MemoryInfo[] memoryArray = getMemoryInfo(context, pids); + if (memoryArray.length > 0) { + return HookUtils.getPssArray(memoryArray[0]); + } else { + return new double[4]; + } + } + + /** + * 单位MB + */ + public static double getPssMemValue(Context context, int[] pids) { + double value = 0.0; + Debug.MemoryInfo[] memoryArray = getMemoryInfo(context, pids); + if (memoryArray.length > 0) { + value = DoubleUtils.keepDecimal(memoryArray[0].getTotalPss() / 1024.0, 1); + } + return value; + } + + /** + * 只能获取本应用的PSS内存占系统百分比:单位%,保留1位小数 + * @return + */ + public static double getPidMemPercent() { + double percent = 0.0; + Debug.MemoryInfo memInfo = new Debug.MemoryInfo(); + Debug.getMemoryInfo(memInfo); + int pidTotalPss = memInfo.getTotalPss(); + long systemTotalMem = getTotalMem(); + percent = DoubleUtils.keepDecimal(pidTotalPss * 100.0 / systemTotalMem, 1); + return percent; + } + + /** + * 获取本应用进程总pss内存,单位MB + */ + public static double getPssMem() { + Debug.MemoryInfo memInfo = new Debug.MemoryInfo(); + Debug.getMemoryInfo(memInfo); + return DoubleUtils.keepDecimal(memInfo.getTotalPss() / 1024.0, 1); + } + + /** + * 获取系统空闲内存占用百分比: 单位% + * @return + */ + public static double getSystemMemPercent() { + double percent = 0.0; + long[] memInfo = getMemInfo(); + long free = memInfo[1] + memInfo[2] + memInfo[3]; + long total = memInfo[0]; + percent = DoubleUtils.keepDecimal(free * 100.0 / total, 1); + return percent; + } + + /** + * 获取空闲内存 + * + * @return 空闲内存,单位KB + */ + public static long getFreeMem() { + long[] memInfo = getMemInfo(); + return memInfo[1] + memInfo[2] + memInfo[3]; + } + + /** + * 获取总内存 + * + * @return 总内存,单位KB + */ + public static long getTotalMem() { + long[] memInfo = getMemInfo(); + return memInfo[0]; + } + + /** + * 获取内存信息:total、free、buffers、cached,单位KB + * + * @return 内存信息 + */ + public static long[] getMemInfo() { + long memInfo[] = new long[4]; + try { + Class procClazz = Class.forName("android.os.Process"); + Class paramTypes[] = new Class[] { String.class, String[].class, + long[].class }; + Method readProclines = procClazz.getMethod("readProcLines", + paramTypes); + Object args[] = new Object[3]; + final String[] memInfoFields = new String[] { "MemTotal:", + "MemFree:", "Buffers:", "Cached:" }; + long[] memInfoSizes = new long[memInfoFields.length]; + memInfoSizes[0] = 30; + memInfoSizes[1] = -30; + args[0] = "/proc/meminfo"; + args[1] = memInfoFields; + args[2] = memInfoSizes; + if (null != readProclines) { + readProclines.invoke(null, args); + for (int i = 0; i < memInfoSizes.length; i++) { + memInfo[i] = memInfoSizes[i]; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return memInfo; + } + + public static long[] getHeapNative() { + int Native_HeapSize = 0; + int Native_HeapAlloc = 1; + long[] value = new long[2]; + value[Native_HeapSize] = Debug.getNativeHeapSize() >> 10; + value[Native_HeapAlloc] = Debug.getNativeHeapAllocatedSize() >> 10; + return value; + } + + public static long[] getHeapDalvik() { + int Total_HeapSize = 0; + int Total_HeapAlloc = 1; + + long[] value_total = new long[2]; + value_total[Total_HeapSize] = Runtime.getRuntime().totalMemory() >> 10; + value_total[Total_HeapAlloc] = (Runtime.getRuntime().totalMemory() - Runtime + .getRuntime().freeMemory()) >> 10; + + long[] value_native = getHeapNative(); + + int Dalvik_HeapSize = 0; + int Dalvik_HeapAlloc = 1; + long[] value_dalvik = new long[2]; + value_dalvik[Dalvik_HeapSize] = value_total[Total_HeapSize] + - value_native[0]; + value_dalvik[Dalvik_HeapAlloc] = value_total[Total_HeapAlloc] + - value_native[1]; + + return value_dalvik; + } + + /** + * 获取堆内存数据,精确到KB Get VM Heap Size by calling: + * Runtime.getRuntime().totalMemory(); Get Allocated VM Memory by calling: + * Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + * Get VM Heap Size Limit by calling: Runtime.getRuntime().maxMemory() Get + * Native Allocated Memory by calling: Debug.getNativeHeapAllocatedSize(); + */ + public static long[] getVM() { + long[] value = new long[5]; + value[0] = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime() + .freeMemory()) >> 10; + value[1] = Runtime.getRuntime().totalMemory() >> 10; + + value[2] = Debug.getNativeHeapAllocatedSize() >> 10; + value[3] = Debug.getNativeHeapSize() >> 10; + value[4] = Debug.getGlobalAllocSize() >> 10; + return value; + } + + private static Debug.MemoryInfo[] getMemoryInfo(Context context, int[] pids) { + ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + return am.getProcessMemoryInfo(pids); + } +} diff --git a/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/MoGoHmiFragment.kt b/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/MoGoHmiFragment.kt index c43b31c32f..25f91f0723 100644 --- a/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/MoGoHmiFragment.kt +++ b/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/MoGoHmiFragment.kt @@ -160,6 +160,8 @@ import java.util.* private var busOperationStatus: IOchBusView? = null + private var isStarted = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } @@ -182,10 +184,17 @@ import java.util.* } } - /*ivCameraIcon?.setOnLongClickListener { - activity?.let { it1 -> CarcorderPreviewView.show(it1) } + ivCameraIcon?.setOnLongClickListener { + if (!isStarted) { + CallerDevaToolsManager.startMonitor() + isStarted = true + } else { + CallerDevaToolsManager.stopMonitor() + isStarted = false + } +// activity?.let { it1 -> CarcorderPreviewView.show(it1) } true - }*/ + } ivToolsIcon?.setOnClickListener { if (toolsViewFloat == null) { diff --git a/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/main/MainMoGoApplication.java b/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/main/MainMoGoApplication.java index b8e0c0560f..fb1ee3827d 100644 --- a/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/main/MainMoGoApplication.java +++ b/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/main/MainMoGoApplication.java @@ -28,8 +28,12 @@ import com.mogo.module.common.MogoModule; import com.mogo.module.common.MogoModulePaths; import com.mogo.module.service.ServiceConst; import com.zhidao.support.obu.ami.AmiClientManager; +import com.zhjt.mogo_core_function_devatools.monitor.db.CpuInfo; +import com.zhjt.mogo_core_function_devatools.monitor.db.MemInfo; +import com.zhjt.mogo_core_function_devatools.monitor.db.MonitorDb; import java.lang.reflect.Field; +import java.util.List; /** * 默认初始化一些基础服务配置 @@ -58,6 +62,7 @@ public abstract class MainMoGoApplication extends AbsMogoApplication { //查询是否有版本的更新 queryAppUpgrade(); + checkMonitorDb(); } @Override @@ -75,6 +80,18 @@ public abstract class MainMoGoApplication extends AbsMogoApplication { },5000); } + private void checkMonitorDb() { + new Thread(() -> { + long limitId = 50001; + List cpuList = MonitorDb.getDb(this).monitorDao().getAllCPUById(limitId); + List memList = MonitorDb.getDb(this).monitorDao().getAllMemById(limitId); + // 大于5w条清除 + if (cpuList.size() > 0 || memList.size() > 0) { + this.deleteDatabase(MonitorDb.INTERNAL_DB_NAME); + } + }).start(); + } + /** * 初始化异常采集配置 */ diff --git a/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/IDevaToolsProvider.kt b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/IDevaToolsProvider.kt index b5f425ca00..4f4bd4b9da 100644 --- a/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/IDevaToolsProvider.kt +++ b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/IDevaToolsProvider.kt @@ -113,4 +113,8 @@ interface IDevaToolsProvider : IProvider { * 隐藏状态栏 */ fun hideStatusBar() + + fun startMonitor() + + fun stopMonitor() } \ No newline at end of file diff --git a/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/devatools/CallerDevaToolsManager.kt b/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/devatools/CallerDevaToolsManager.kt index 79041199d0..ddf0921a3d 100644 --- a/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/devatools/CallerDevaToolsManager.kt +++ b/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/devatools/CallerDevaToolsManager.kt @@ -164,4 +164,12 @@ object CallerDevaToolsManager { devaToolsProviderApi?.hideStatusBar() } } + + fun startMonitor() { + devaToolsProviderApi?.startMonitor() + } + + fun stopMonitor() { + devaToolsProviderApi?.stopMonitor() + } } \ No newline at end of file