[6.2.0][技术优化] 更改Handler消息记录方式,对编译生成的类方法集合进行过滤
This commit is contained in:
@@ -214,9 +214,10 @@ dependencies {
|
|||||||
implementation rootProject.ext.dependencies.android_start_up
|
implementation rootProject.ext.dependencies.android_start_up
|
||||||
implementation rootProject.ext.dependencies.lancetx_runtime
|
implementation rootProject.ext.dependencies.lancetx_runtime
|
||||||
implementation rootProject.ext.dependencies.lancetx_compiler_lib
|
implementation rootProject.ext.dependencies.lancetx_compiler_lib
|
||||||
//kapt rootProject.ext.dependencies.lancetx_compiler
|
implementation rootProject.ext.dependencies.handler_proxy_runtime
|
||||||
|
kapt rootProject.ext.dependencies.lancetx_compiler
|
||||||
|
|
||||||
//annotationProcessor rootProject.ext.dependencies.lancetx_compiler
|
annotationProcessor rootProject.ext.dependencies.lancetx_compiler
|
||||||
annotationProcessor rootProject.ext.dependencies.google_auto_service
|
annotationProcessor rootProject.ext.dependencies.google_auto_service
|
||||||
kapt rootProject.ext.dependencies.google_auto_service
|
kapt rootProject.ext.dependencies.google_auto_service
|
||||||
compileOnly rootProject.ext.dependencies.google_auto_service
|
compileOnly rootProject.ext.dependencies.google_auto_service
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
-keeppackage com/knightboost/lancet/api/synchronized_lock/
|
-keeppackage com/knightboost/lancet/api/synchronized_lock/
|
||||||
-keeppackage com/mogo/core/lancetx/compiler/lib/
|
-keeppackage com/mogo/core/lancetx/compiler/lib/
|
||||||
-keeppackage com/mogo/eagle/core/lancetx/generated/
|
-keeppackage com/mogo/eagle/core/lancetx/generated/
|
||||||
|
-keeppackage com/mogo/eagle/core/handler/
|
||||||
-keeppackage com/mogo/launcher/lancet/jank/
|
-keeppackage com/mogo/launcher/lancet/jank/
|
||||||
-keeppackage com/rousetime/android_startup/
|
-keeppackage com/rousetime/android_startup/
|
||||||
-keeppackage com/mogo/systrace/
|
-keeppackage com/mogo/systrace/
|
||||||
@@ -22,4 +23,5 @@
|
|||||||
-keepclass com.mogo.commons.AbsMogoApplication
|
-keepclass com.mogo.commons.AbsMogoApplication
|
||||||
-keepclass com.bytedance.apm.agent.v2.instrumentation.AppAgent
|
-keepclass com.bytedance.apm.agent.v2.instrumentation.AppAgent
|
||||||
-keepclass com.mogo.eagle.core.utilcode.util.ProcessUtils
|
-keepclass com.mogo.eagle.core.utilcode.util.ProcessUtils
|
||||||
-keepclass com.mogo.eagle.core.utilcode.mogo.AppLaunchTimeUtils
|
-keepclass com.mogo.eagle.core.utilcode.mogo.AppLaunchTimeUtils
|
||||||
|
-keepclass com.mogo.eagle.core.function.main.ARouterUtils
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package com.mogo.launcher.lancet.jank;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
|
|
||||||
import androidx.annotation.Keep;
|
|
||||||
|
|
||||||
import com.knightboost.lancet.api.Scope;
|
|
||||||
import com.knightboost.lancet.api.annotations.Group;
|
|
||||||
import com.knightboost.lancet.api.annotations.ReplaceInvoke;
|
|
||||||
import com.knightboost.lancet.api.annotations.TargetClass;
|
|
||||||
import com.knightboost.lancet.api.annotations.TargetMethod;
|
|
||||||
import com.knightboost.lancet.api.annotations.Weaver;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
|
||||||
|
|
||||||
|
|
||||||
@Keep
|
|
||||||
@Weaver
|
|
||||||
@Group("main_block_check")
|
|
||||||
public class AsyncHandlerReflectLancet {
|
|
||||||
|
|
||||||
|
|
||||||
@TargetClass(value = "java.lang.reflect.Method",scope = Scope.SELF)
|
|
||||||
@TargetMethod(methodName = "invoke")
|
|
||||||
@ReplaceInvoke
|
|
||||||
public static Object hookMethodInvoke(Method method, Object obj, Object... args) throws InvocationTargetException, IllegalAccessException {
|
|
||||||
Object ret = method.invoke(obj, args);
|
|
||||||
if (ret instanceof Handler && "createAsync".equals(method.getName())) {
|
|
||||||
CopyOnWriteArraySet<WeakReference<Handler>> asyncHandlers = MainBlockCheck.Companion.getAsyncHandlers();
|
|
||||||
asyncHandlers.add(new WeakReference<>((Handler) ret));
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@TargetClass(value = "android.os.Handler",scope = Scope.SELF)
|
|
||||||
@TargetMethod(methodName = "createAsync")
|
|
||||||
@ReplaceInvoke(isStatic = true)
|
|
||||||
public static Handler createAsync(Looper looper) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
||||||
Handler handler = Handler.createAsync(looper);
|
|
||||||
CopyOnWriteArraySet<WeakReference<Handler>> asyncHandlers = MainBlockCheck.Companion.getAsyncHandlers();
|
|
||||||
asyncHandlers.add(new WeakReference<>(handler));
|
|
||||||
return handler;
|
|
||||||
} else {
|
|
||||||
return new Handler(looper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetClass(value = "android.os.Handler",scope = Scope.SELF)
|
|
||||||
@TargetMethod(methodName = "createAsync")
|
|
||||||
@ReplaceInvoke(isStatic = true)
|
|
||||||
public static Handler createAsync(Looper looper, Handler.Callback callback) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
||||||
Handler handler = Handler.createAsync(looper, callback);
|
|
||||||
CopyOnWriteArraySet<WeakReference<Handler>> asyncHandlers = MainBlockCheck.Companion.getAsyncHandlers();
|
|
||||||
asyncHandlers.add(new WeakReference<>(handler));
|
|
||||||
return handler;
|
|
||||||
} else {
|
|
||||||
return new Handler(looper, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,705 +0,0 @@
|
|||||||
package com.mogo.launcher.lancet.jank
|
|
||||||
|
|
||||||
import android.app.*
|
|
||||||
import android.content.*
|
|
||||||
import android.graphics.*
|
|
||||||
import android.os.*
|
|
||||||
import android.os.Build.VERSION
|
|
||||||
import android.os.Build.VERSION_CODES
|
|
||||||
import android.os.Handler.Callback
|
|
||||||
import android.util.*
|
|
||||||
import android.view.*
|
|
||||||
import androidx.annotation.*
|
|
||||||
import androidx.core.os.HandlerCompat
|
|
||||||
import androidx.core.util.Pools
|
|
||||||
import com.knightboost.lancet.api.*
|
|
||||||
import com.knightboost.lancet.api.Scope.ALL
|
|
||||||
import com.knightboost.lancet.api.Scope.LEAF
|
|
||||||
import com.knightboost.lancet.api.annotations.*
|
|
||||||
import com.knightboost.lancet.api.annotations.Weaver
|
|
||||||
import com.mogo.eagle.core.block.runtime.config.recorder.*
|
|
||||||
import com.mogo.eagle.core.block.runtime.utils.TimeUtils.Companion.now
|
|
||||||
import com.mogo.eagle.core.function.call.devatools.*
|
|
||||||
import com.mogo.eagle.core.utilcode.util.*
|
|
||||||
import kotlinx.coroutines.Runnable
|
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import java.util.concurrent.*
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
|
||||||
import kotlin.concurrent.withLock
|
|
||||||
import com.mogo.eagle.core.block.runtime.message.Message as Holder
|
|
||||||
|
|
||||||
@Keep
|
|
||||||
@Weaver
|
|
||||||
@Group("main_block_check")
|
|
||||||
class MainBlockCheck {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
private const val TAG = "MAIN_BLOCK_CHECK"
|
|
||||||
|
|
||||||
private val handlerTokens by lazy { ConcurrentHashMap<String, CopyOnWriteArrayList<Holder>>() }
|
|
||||||
|
|
||||||
private val pool by lazy { Pools.SimplePool<ActionWrapper>(50) }
|
|
||||||
|
|
||||||
private val actions by lazy { ConcurrentHashMap<Runnable, ActionWrapper>() }
|
|
||||||
|
|
||||||
private val lock by lazy { ReentrantReadWriteLock() }
|
|
||||||
|
|
||||||
val asyncHandlers by lazy { CopyOnWriteArraySet<WeakReference<Handler>>() }
|
|
||||||
|
|
||||||
private val handlers by lazy { ConcurrentHashMap<Int, Boolean>() }
|
|
||||||
|
|
||||||
private val handlerWhats by lazy { ConcurrentHashMap<Int, CopyOnWriteArraySet<Int>>() }
|
|
||||||
|
|
||||||
private val main by lazy { Looper.getMainLooper() }
|
|
||||||
|
|
||||||
private fun acquireAction(msg: Holder, action: Runnable): ActionWrapper {
|
|
||||||
return lock.readLock().withLock {
|
|
||||||
val ret = pool.acquire() ?: ActionWrapper(msg, action)
|
|
||||||
ret.msg = msg
|
|
||||||
ret.action = action
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun releaseAction(wrapper: ActionWrapper) {
|
|
||||||
lock.writeLock().withLock { pool.release(wrapper) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class ActionWrapper(var msg: Holder, var action: Runnable) : Runnable {
|
|
||||||
override fun run() {
|
|
||||||
var elapsedTime: Long = 0
|
|
||||||
var startTime: Long = 0
|
|
||||||
try {
|
|
||||||
val expect = msg.enqueueTime + msg.delay
|
|
||||||
val now = now()
|
|
||||||
elapsedTime = now - expect
|
|
||||||
startTime = now()
|
|
||||||
action.run()
|
|
||||||
} catch (t: Throwable) {
|
|
||||||
Log.e(TAG, "msg -> $msg", t)
|
|
||||||
throw t
|
|
||||||
} finally {
|
|
||||||
getRecorder()?.recycle(msg.id, elapsedTime, now() - startTime)
|
|
||||||
releaseAction(this)
|
|
||||||
actions.remove(action)
|
|
||||||
CallerDevaToolsManager.usage()?.updateMainThreadTime(Debug.threadCpuTimeNanos())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return action.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun getRecorder(): IMessageRecorder? {
|
|
||||||
val block = CallerDevaToolsManager.block()
|
|
||||||
if (block == null || !block.hasInit()) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return block.recorder()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic val callback by lazy {
|
|
||||||
Callback { msg ->
|
|
||||||
val enclose = msg.obj as? Holder
|
|
||||||
if (enclose == null) {
|
|
||||||
Log.w(TAG, "--- handleMessage --- 1 ---")
|
|
||||||
return@Callback true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!enclose.isValid()) {
|
|
||||||
Log.w(TAG, "--- handleMessage --- 2 ---:$enclose")
|
|
||||||
return@Callback true
|
|
||||||
}
|
|
||||||
val h = enclose.handler
|
|
||||||
val action = enclose.action
|
|
||||||
val oldMsg = enclose.origin
|
|
||||||
val oldObj = enclose.obj
|
|
||||||
val start = now()
|
|
||||||
try {
|
|
||||||
if (action != null) {
|
|
||||||
action.run()
|
|
||||||
} else {
|
|
||||||
if (oldMsg == null) {
|
|
||||||
h?.dispatchMessage(msg)
|
|
||||||
} else {
|
|
||||||
oldMsg.obj = oldObj
|
|
||||||
h?.dispatchMessage(oldMsg)
|
|
||||||
}
|
|
||||||
if (h != null) {
|
|
||||||
val key = generateKey(h, msg.what)
|
|
||||||
handlerTokens[key]?.remove(enclose)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (t: Throwable) {
|
|
||||||
Log.e(TAG, "msg: $enclose", t)
|
|
||||||
throw t
|
|
||||||
} finally {
|
|
||||||
val now = now()
|
|
||||||
val duration = now - start
|
|
||||||
val expect = enclose.enqueueTime + enclose.delay
|
|
||||||
val elapsed = now - expect
|
|
||||||
getRecorder()?.recycle(enclose.id, elapsed, duration)
|
|
||||||
CallerDevaToolsManager.usage()?.updateMainThreadTime(Debug.threadCpuTimeNanos())
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic val ASYNC_HANDLER by lazy {
|
|
||||||
HandlerCompat.createAsync(main, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic val HANDLER by lazy {
|
|
||||||
Handler(main, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "post")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerPost(handler: Handler, action: Runnable): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.post(action)
|
|
||||||
}
|
|
||||||
val holder = Holder.acquire(handler, action = action, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
|
|
||||||
val inserted = handler.post(acquireAction(holder, action).also { actions[action] = it })
|
|
||||||
if (inserted) {
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
} else {
|
|
||||||
actions.remove(action)
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "postDelayed")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerPostDelay(handler: Handler, action: Runnable, delayMillis: Long): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.postDelayed(action, delayMillis)
|
|
||||||
}
|
|
||||||
val holder = Holder.acquire(handler, action = action, delay = delayMillis, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
|
|
||||||
val inserted = handler.postDelayed(acquireAction(holder, action).also { actions[action] = it }, delayMillis)
|
|
||||||
if (inserted) {
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
} else {
|
|
||||||
actions.remove(action)
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "postDelayed")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerPostDelayWithToken(handler: Handler, action: Runnable, token: Any?, delayMillis: Long): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return HandlerCompat.postDelayed(handler, action, token, delayMillis)
|
|
||||||
}
|
|
||||||
val holder = Holder.acquire(handler, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) }, action = action, obj = token, delay = delayMillis)
|
|
||||||
val inserted = HandlerCompat.postDelayed(handler, acquireAction(holder, action).also { actions[action] = it }, token, delayMillis)
|
|
||||||
if (inserted) {
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
} else {
|
|
||||||
actions.remove(action)
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "postAtFrontOfQueue")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerPostAtFrontOfQueue(handler: Handler, action: Runnable): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.postAtFrontOfQueue(action)
|
|
||||||
}
|
|
||||||
val holder = Holder.acquire(handler, action = action, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
|
|
||||||
val inserted = handler.postAtFrontOfQueue(acquireAction(holder, action).also { actions[action] = it })
|
|
||||||
if (inserted) {
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
} else {
|
|
||||||
actions.remove(action)
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "postAtTime")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerPostAtTime(handler: Handler, action: Runnable, uptimeMillis: Long): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.postAtTime(action, uptimeMillis)
|
|
||||||
}
|
|
||||||
val delta = uptimeMillis - SystemClock.uptimeMillis()
|
|
||||||
val holder = Holder.acquire(handler, action = action, delay = if (delta > 0) delta else 0, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
|
|
||||||
val inserted = handler.postAtTime(acquireAction(holder, action), uptimeMillis)
|
|
||||||
if (inserted) {
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
} else {
|
|
||||||
actions.remove(action)
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "postAtTime")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerPostAtTimeWithToken(handler: Handler, action: Runnable, token: Any?, uptimeMillis: Long): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.postAtTime(action, uptimeMillis)
|
|
||||||
}
|
|
||||||
val delta = uptimeMillis - SystemClock.uptimeMillis()
|
|
||||||
val holder = Holder.acquire(handler, action = action, delay = if (delta > 0) delta else 0, obj = token, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
|
|
||||||
val inserted = handler.postAtTime(acquireAction(holder, action).also { actions[action] = it }, token, uptimeMillis)
|
|
||||||
if (inserted) {
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
} else {
|
|
||||||
actions.remove(action)
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "sendEmptyMessage")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerSendEmptyMessage(handler: Handler, what: Int): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.sendEmptyMessage(what)
|
|
||||||
}
|
|
||||||
val key = handler.hashCode()
|
|
||||||
val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) }
|
|
||||||
val newHandler = getHandler(isAsync)
|
|
||||||
val msg = Message.obtain(newHandler, what)
|
|
||||||
val holder = Holder.acquire(handler, what = what, isAsync = isAsync)
|
|
||||||
msg.obj = holder
|
|
||||||
val tokenKey = generateKey(handler, what)
|
|
||||||
handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder)
|
|
||||||
val inserted = newHandler.sendMessage(msg)
|
|
||||||
if (inserted) {
|
|
||||||
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(what)
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
} else {
|
|
||||||
handlerTokens[tokenKey]?.remove(holder)
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "sendEmptyMessageAtTime")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerSendEmptyMessageAtTime(handler: Handler, what: Int, uptimeMillis: Long): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.sendEmptyMessageAtTime(what, uptimeMillis)
|
|
||||||
}
|
|
||||||
val key = handler.hashCode()
|
|
||||||
val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) }
|
|
||||||
val newHandler = getHandler(isAsync)
|
|
||||||
val msg = Message.obtain(newHandler, what)
|
|
||||||
val delta = uptimeMillis - SystemClock.uptimeMillis()
|
|
||||||
val holder = Holder.acquire(handler, what = what, delay = if (delta > 0) delta else 0, isAsync = isAsync)
|
|
||||||
msg.obj = holder
|
|
||||||
val tokenKey = generateKey(handler, what)
|
|
||||||
handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder)
|
|
||||||
val inserted = newHandler.sendMessageAtTime(msg, uptimeMillis)
|
|
||||||
if (inserted) {
|
|
||||||
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(what)
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
} else {
|
|
||||||
handlerTokens[tokenKey]?.remove(holder)
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "sendEmptyMessageDelayed")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerSendEmptyMessageDelayed(handler: Handler, what: Int, delayMillis: Long): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.sendEmptyMessageDelayed(what, delayMillis)
|
|
||||||
}
|
|
||||||
val key = handler.hashCode()
|
|
||||||
val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) }
|
|
||||||
val newHandler = getHandler(isAsync)
|
|
||||||
val msg = Message.obtain(newHandler, what)
|
|
||||||
val holder = Holder.acquire(handler, what = what, delay = delayMillis, isAsync = isAsync)
|
|
||||||
msg.obj = holder
|
|
||||||
val tokenKey = generateKey(handler, what)
|
|
||||||
handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder)
|
|
||||||
val inserted = newHandler.sendMessageDelayed(msg, delayMillis)
|
|
||||||
if (inserted) {
|
|
||||||
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(what)
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
} else {
|
|
||||||
handlerTokens[tokenKey]?.remove(holder)
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "sendMessage")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerSendMessage(handler: Handler, msg: Message): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.sendMessage(msg)
|
|
||||||
}
|
|
||||||
val tokenKey = generateKey(handler, msg.what)
|
|
||||||
val key = handler.hashCode()
|
|
||||||
val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) }
|
|
||||||
val action = msg.callback
|
|
||||||
val holder = Holder.acquire(handler, originMsg = msg, action = msg.callback, obj = msg.obj, what = msg.what, isAsync = isAsync)
|
|
||||||
val inserted: Boolean
|
|
||||||
if (action != null) {
|
|
||||||
inserted = handler.post(acquireAction(holder, action).also { actions[action] = it })
|
|
||||||
} else {
|
|
||||||
msg.obj = holder
|
|
||||||
handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder)
|
|
||||||
val newHandler = getHandler(isAsync)
|
|
||||||
inserted = newHandler.sendMessage(msg)
|
|
||||||
}
|
|
||||||
if (inserted) {
|
|
||||||
if (action == null) {
|
|
||||||
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(holder.what)
|
|
||||||
}
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
} else {
|
|
||||||
if (action == null) {
|
|
||||||
handlerTokens[tokenKey]?.remove(holder)
|
|
||||||
} else {
|
|
||||||
actions.remove(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "sendMessageAtTime")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerSendMessageAtTime(handler: Handler, msg: Message, uptimeMillis: Long): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.sendMessageAtTime(msg, uptimeMillis)
|
|
||||||
}
|
|
||||||
val tokenKey = generateKey(handler, msg.what)
|
|
||||||
val key = handler.hashCode()
|
|
||||||
val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) }
|
|
||||||
val action = msg.callback
|
|
||||||
val delta = uptimeMillis - SystemClock.uptimeMillis()
|
|
||||||
val holder = Holder.acquire(handler, originMsg = msg, action = msg.callback, obj = msg.obj, what = msg.what, isAsync = isAsync, delay = if (delta > 0) delta else 0)
|
|
||||||
val inserted: Boolean
|
|
||||||
if (action != null) {
|
|
||||||
inserted = handler.postAtTime(acquireAction(holder, action).also { actions[action] = it }, uptimeMillis)
|
|
||||||
} else {
|
|
||||||
msg.obj = holder
|
|
||||||
handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder)
|
|
||||||
val newHandler = getHandler(isAsync)
|
|
||||||
inserted = newHandler.sendMessageAtTime(msg, uptimeMillis)
|
|
||||||
}
|
|
||||||
if (inserted) {
|
|
||||||
if (action == null) {
|
|
||||||
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(holder.what)
|
|
||||||
}
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
} else {
|
|
||||||
if (action == null) {
|
|
||||||
handlerTokens[tokenKey]?.remove(holder)
|
|
||||||
} else {
|
|
||||||
actions.remove(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "sendMessageAtFrontOfQueue")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerSendMessageAtFrontOfQueue(handler: Handler, msg: Message): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.sendMessageAtFrontOfQueue(msg)
|
|
||||||
}
|
|
||||||
val tokenKey = generateKey(handler, msg.what)
|
|
||||||
val key = handler.hashCode()
|
|
||||||
val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) }
|
|
||||||
val action = msg.callback
|
|
||||||
val holder = Holder.acquire(handler, originMsg = msg, action = msg.callback, obj = msg.obj, what = msg.what, isAsync = isAsync)
|
|
||||||
val inserted: Boolean
|
|
||||||
if (action != null) {
|
|
||||||
inserted = handler.postAtFrontOfQueue(acquireAction(holder, action).also { actions[action] = it })
|
|
||||||
} else {
|
|
||||||
msg.obj = holder
|
|
||||||
handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder)
|
|
||||||
val newHandler = getHandler(isAsync)
|
|
||||||
inserted = newHandler.sendMessageAtFrontOfQueue(msg)
|
|
||||||
}
|
|
||||||
if (inserted) {
|
|
||||||
if (action == null) {
|
|
||||||
handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(holder.what)
|
|
||||||
}
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
} else {
|
|
||||||
if (action == null) {
|
|
||||||
handlerTokens[tokenKey]?.remove(holder)
|
|
||||||
} else {
|
|
||||||
actions.remove(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "hasCallbacks")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerHasCallbacks(handler: Handler, action: Runnable): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return if (VERSION.SDK_INT >= VERSION_CODES.Q) {
|
|
||||||
handler.hasCallbacks(action)
|
|
||||||
} else {
|
|
||||||
HandlerCompat.hasCallbacks(handler, action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val existed = actions[action]
|
|
||||||
if (existed != null) {
|
|
||||||
return HandlerCompat.hasCallbacks(handler, existed)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "hasMessages")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerHasMessages(handler: Handler, what: Int): Boolean {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.hasMessages(what)
|
|
||||||
}
|
|
||||||
return getHandler(computeIsAsyncHandler(handler)).hasMessages(what)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.app.Activity", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "post")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun runOnUiThreadOfActivity(activity: Activity, action: Runnable) {
|
|
||||||
val holder = Holder.acquire(null, action = action)
|
|
||||||
try {
|
|
||||||
activity.runOnUiThread(acquireAction(holder, action))
|
|
||||||
} finally {
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.view.View", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "post")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun postOfView(view: View, action: Runnable): Boolean {
|
|
||||||
val holder = Holder.acquire(null, action = action)
|
|
||||||
val inserted = view.post(acquireAction(holder, action).also { actions[action] = it })
|
|
||||||
if (inserted) {
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.view.View", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "postDelayed")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun postDelayedOfView(view: View, action: Runnable, delayMillis: Long): Boolean {
|
|
||||||
val holder = Holder.acquire(null, action = action, delay = delayMillis)
|
|
||||||
val inserted = view.postDelayed(acquireAction(holder, action).also { actions[action] = it }, delayMillis)
|
|
||||||
if (inserted) {
|
|
||||||
getRecorder()?.insert(holder)
|
|
||||||
}
|
|
||||||
return inserted
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "removeCallbacks")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerRemoveCallbacks(handler: Handler, action: Runnable) {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.removeCallbacks(action)
|
|
||||||
}
|
|
||||||
val existed = actions.remove(action)
|
|
||||||
if (existed != null) {
|
|
||||||
try {
|
|
||||||
handler.removeCallbacks(existed)
|
|
||||||
} finally {
|
|
||||||
getRecorder()?.remove(existed.msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "removeCallbacks")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerRemoveCallbacksWithToken(handler: Handler, action: Runnable, token: Any?) {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.removeCallbacks(action, token)
|
|
||||||
}
|
|
||||||
val existed = actions.remove(action)
|
|
||||||
if (existed != null) {
|
|
||||||
try {
|
|
||||||
handler.removeCallbacks(existed, token)
|
|
||||||
} finally {
|
|
||||||
getRecorder()?.remove(existed.msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "removeMessages")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerRemoveMessages(handler: Handler, what: Int) {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.removeMessages(what)
|
|
||||||
}
|
|
||||||
val newHandler = getHandler(handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
|
|
||||||
val tokens = handlerTokens.remove(generateKey(handler, what))
|
|
||||||
if (!tokens.isNullOrEmpty()) {
|
|
||||||
val snapshot = ArrayList<Holder>(tokens)
|
|
||||||
snapshot.forEach {
|
|
||||||
newHandler.removeMessages(what, it)
|
|
||||||
}
|
|
||||||
getRecorder()?.remove(snapshot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "removeCallbacksAndMessages")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerRemoveCallbacksAndMessages(handler: Handler, token: Any?) {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.removeCallbacksAndMessages(token)
|
|
||||||
}
|
|
||||||
handler.removeCallbacksAndMessages(token)
|
|
||||||
val existed = handlerWhats[handler.hashCode()]
|
|
||||||
if (!existed.isNullOrEmpty()) {
|
|
||||||
existed.forEach {what ->
|
|
||||||
handlerTokens.remove(generateKey(handler, what))?.forEach { msg ->
|
|
||||||
getHandler(handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) }).removeMessages(what, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.view.View", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "removeCallbacks")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun viewRemoveCallback(view: View, action: Runnable): Boolean {
|
|
||||||
val exist = actions.remove(action)
|
|
||||||
if (exist != null) {
|
|
||||||
try {
|
|
||||||
return view.removeCallbacks(exist)
|
|
||||||
} finally {
|
|
||||||
getRecorder()?.remove(exist.msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@TargetClass(value = "android.os.Handler", scope = ALL)
|
|
||||||
@TargetMethod(methodName = "removeMessages")
|
|
||||||
@ReplaceInvoke
|
|
||||||
fun handlerRemoveMessagesWithToken(handler: Handler, what: Int, token: Any?) {
|
|
||||||
if (main != handler.looper) {
|
|
||||||
return handler.removeMessages(what, token)
|
|
||||||
}
|
|
||||||
val newHandler = getHandler(handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) })
|
|
||||||
val tokens = handlerTokens.remove(generateKey(handler, what))
|
|
||||||
if (!tokens.isNullOrEmpty()) {
|
|
||||||
val snapshot = ArrayList<Holder>(tokens)
|
|
||||||
snapshot.forEach {
|
|
||||||
newHandler.removeMessages(what, it)
|
|
||||||
}
|
|
||||||
getRecorder()?.remove(snapshot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateKey(handler: Handler, what: Int) : String {
|
|
||||||
return "${handler.hashCode()}_$what"
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun computeIsAsyncHandler(handler: Handler): Boolean {
|
|
||||||
return asyncHandlers.find { it.get() === handler } != null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getHandler(isAsync: Boolean): Handler {
|
|
||||||
return if (isAsync) {
|
|
||||||
ASYNC_HANDLER
|
|
||||||
} else {
|
|
||||||
HANDLER
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetClass(value = "android.app.Activity", scope = LEAF)
|
|
||||||
@Insert(mayCreateSuper = true)
|
|
||||||
@TargetMethod(methodName = "onCreate")
|
|
||||||
fun proxyActivityOnCreate(savedInstanceState: Bundle?) {
|
|
||||||
try {
|
|
||||||
Origin.callVoid()
|
|
||||||
} finally {
|
|
||||||
CallerDevaToolsManager.block()?.monitor((This.get() as Activity).window)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@TargetClass(value = "android.app.Activity", scope = LEAF)
|
|
||||||
@Insert(mayCreateSuper = true)
|
|
||||||
@TargetMethod(methodName = "onResume")
|
|
||||||
fun proxyActivityOnResume() {
|
|
||||||
try {
|
|
||||||
Origin.callVoid()
|
|
||||||
} finally {
|
|
||||||
CallerDevaToolsManager.block()?.resume((This.get() as Activity).window)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetClass(value = "android.app.Activity", scope = LEAF)
|
|
||||||
@Insert(mayCreateSuper = true)
|
|
||||||
@TargetMethod(methodName = "onPause")
|
|
||||||
fun proxyActivityOnPause() {
|
|
||||||
try {
|
|
||||||
Origin.callVoid()
|
|
||||||
} finally {
|
|
||||||
CallerDevaToolsManager.block()?.pause((This.get() as Activity).window)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetClass(value = "android.app.Activity", scope = LEAF)
|
|
||||||
@Insert(mayCreateSuper = true)
|
|
||||||
@TargetMethod(methodName = "onDestroy")
|
|
||||||
fun proxyActivityOnDestroy() {
|
|
||||||
try {
|
|
||||||
Origin.callVoid()
|
|
||||||
} finally {
|
|
||||||
CallerDevaToolsManager.block()?.pop((This.get() as Activity).window)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,7 @@ import android.app.Service;
|
|||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.opengl.GLSurfaceView;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewManager;
|
import android.view.ViewManager;
|
||||||
@@ -29,18 +30,14 @@ class JankPointAutoGenerator {
|
|||||||
@LancetXGenerator(
|
@LancetXGenerator(
|
||||||
group = "main_block_check",
|
group = "main_block_check",
|
||||||
type = Type.IPC,
|
type = Type.IPC,
|
||||||
hookAllPublicMethods = true,
|
hookAllPublicMethods = true
|
||||||
excludeHookMethodNames = { "toString", "hashCode" },
|
|
||||||
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
|
|
||||||
)
|
)
|
||||||
private ActivityManager am;
|
private ActivityManager am;
|
||||||
|
|
||||||
@LancetXGenerator(
|
@LancetXGenerator(
|
||||||
group = "main_block_check",
|
group = "main_block_check",
|
||||||
type = Type.IPC,
|
type = Type.IPC,
|
||||||
hookAllPublicMethods = true,
|
hookAllPublicMethods = true
|
||||||
excludeHookMethodNames = { "toString", "hashCode" },
|
|
||||||
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
|
|
||||||
)
|
)
|
||||||
private PackageManager pm;
|
private PackageManager pm;
|
||||||
|
|
||||||
@@ -51,8 +48,28 @@ class JankPointAutoGenerator {
|
|||||||
hookAllPublicMethods = true,
|
hookAllPublicMethods = true,
|
||||||
scope = Scope.LEAF,
|
scope = Scope.LEAF,
|
||||||
hookType = HookType.AROUND,
|
hookType = HookType.AROUND,
|
||||||
excludeHookMethodNames = { "runOnUiThread", "toString", "hashCode", "onPictureInPictureUiStateChanged", "enterPictureInPictureMode", "setPictureInPictureParams" },
|
onlyHookMethodNames = {
|
||||||
excludeHookMethodDescs = { "(Ljava/lang/Runnable;)V", "()Ljava/lang/String;", "()I", "(Landroid/app/PictureInPictureUiState;)V", "(Landroid/app/PictureInPictureParams;)Z", "(Landroid/app/PictureInPictureParams;)V" }
|
"onCreate",
|
||||||
|
"onStart",
|
||||||
|
"onResume",
|
||||||
|
"onPause",
|
||||||
|
"onStop",
|
||||||
|
"onDestroy",
|
||||||
|
"onWindowFocusChanged",
|
||||||
|
"dispatchKeyEvent",
|
||||||
|
"dispatchTouchEvent"
|
||||||
|
},
|
||||||
|
onlyHookMethodDescs = {
|
||||||
|
"(Landroid/os/Bundle;)V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
"(Z)V",
|
||||||
|
"(Landroid/view/KeyEvent;)Z",
|
||||||
|
"(Landroid/view/MotionEvent;)Z"
|
||||||
|
}
|
||||||
)
|
)
|
||||||
private Activity activity;
|
private Activity activity;
|
||||||
|
|
||||||
@@ -63,8 +80,12 @@ class JankPointAutoGenerator {
|
|||||||
hookType = HookType.AROUND,
|
hookType = HookType.AROUND,
|
||||||
hookAllPublicMethods = true,
|
hookAllPublicMethods = true,
|
||||||
scope = Scope.LEAF,
|
scope = Scope.LEAF,
|
||||||
excludeHookMethodNames = { "toString", "hashCode" },
|
onlyHookMethodNames = {
|
||||||
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
|
"onReceive"
|
||||||
|
},
|
||||||
|
onlyHookMethodDescs = {
|
||||||
|
"(Landroid/content/Context;Landroid/content/Intent;)V"
|
||||||
|
}
|
||||||
)
|
)
|
||||||
private BroadcastReceiver receiver;
|
private BroadcastReceiver receiver;
|
||||||
|
|
||||||
@@ -75,8 +96,26 @@ class JankPointAutoGenerator {
|
|||||||
hookType = HookType.AROUND,
|
hookType = HookType.AROUND,
|
||||||
hookAllPublicMethods = true,
|
hookAllPublicMethods = true,
|
||||||
scope = Scope.LEAF,
|
scope = Scope.LEAF,
|
||||||
excludeHookMethodNames = { "toString", "hashCode" },
|
onlyHookMethodNames = {
|
||||||
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
|
"attachInfo",
|
||||||
|
"onCreate",
|
||||||
|
"query",
|
||||||
|
"getType",
|
||||||
|
"insert",
|
||||||
|
"update",
|
||||||
|
"delete",
|
||||||
|
"openFile"
|
||||||
|
},
|
||||||
|
onlyHookMethodDescs = {
|
||||||
|
"(Landroid/content/Context;Landroid/content/pm/ProviderInfo;)V",
|
||||||
|
"()Z",
|
||||||
|
"(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;",
|
||||||
|
"(Landroid/net/Uri;)Ljava/lang/String;",
|
||||||
|
"(Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;",
|
||||||
|
"(Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I",
|
||||||
|
"(Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I",
|
||||||
|
"(Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;"
|
||||||
|
}
|
||||||
)
|
)
|
||||||
private ContentProvider provider;
|
private ContentProvider provider;
|
||||||
|
|
||||||
@@ -87,8 +126,32 @@ class JankPointAutoGenerator {
|
|||||||
mayCreateSuper = true,
|
mayCreateSuper = true,
|
||||||
hookType = HookType.AROUND,
|
hookType = HookType.AROUND,
|
||||||
scope = Scope.LEAF,
|
scope = Scope.LEAF,
|
||||||
excludeHookMethodNames = { "toString", "hashCode" },
|
onlyHookMethodNames = {
|
||||||
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
|
"onCreate",
|
||||||
|
"onStart",
|
||||||
|
"onStartCommand",
|
||||||
|
"onDestroy",
|
||||||
|
"onConfigurationChanged",
|
||||||
|
"onLowMemory",
|
||||||
|
"onTrimMemory",
|
||||||
|
"onBind",
|
||||||
|
"onUnbind",
|
||||||
|
"onRebind",
|
||||||
|
"onTaskRemoved"
|
||||||
|
},
|
||||||
|
onlyHookMethodDescs = {
|
||||||
|
"()V",
|
||||||
|
"(Landroid/content/Intent;I)V",
|
||||||
|
"(Landroid/content/Intent;II)I",
|
||||||
|
"()V",
|
||||||
|
"(Landroid/content/res/Configuration;)V",
|
||||||
|
"()V",
|
||||||
|
"(I)V",
|
||||||
|
"(Landroid/content/Intent;)Landroid/os/IBinder;",
|
||||||
|
"(Landroid/content/Intent;)Z",
|
||||||
|
"(Landroid/content/Intent;)V",
|
||||||
|
"(Landroid/content/Intent;)V",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
private Service service;
|
private Service service;
|
||||||
|
|
||||||
@@ -106,8 +169,24 @@ class JankPointAutoGenerator {
|
|||||||
group = "main_block_check",
|
group = "main_block_check",
|
||||||
type = Type.SLEEP,
|
type = Type.SLEEP,
|
||||||
scope = Scope.ALL,
|
scope = Scope.ALL,
|
||||||
onlyHookMethodNames = { "sleep", "sleep"},
|
onlyHookMethodNames = {
|
||||||
onlyHookMethodDescs = { "(J)V", "(JI)V"}
|
"sleep",
|
||||||
|
"sleep",
|
||||||
|
"join",
|
||||||
|
"join",
|
||||||
|
"join",
|
||||||
|
"getStackTrace",
|
||||||
|
"getAllStackTraces"
|
||||||
|
},
|
||||||
|
onlyHookMethodDescs = {
|
||||||
|
"(J)V",
|
||||||
|
"(JI)V",
|
||||||
|
"(J)V",
|
||||||
|
"(JI)V",
|
||||||
|
"()V",
|
||||||
|
"()[Ljava/lang/StackTraceElement;",
|
||||||
|
"()Ljava/util/Map;"
|
||||||
|
}
|
||||||
)
|
)
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
|
|
||||||
@@ -118,9 +197,7 @@ class JankPointAutoGenerator {
|
|||||||
hookType = HookType.AROUND,
|
hookType = HookType.AROUND,
|
||||||
scope = Scope.LEAF,
|
scope = Scope.LEAF,
|
||||||
mayCreateSuper = true,
|
mayCreateSuper = true,
|
||||||
hookAllPublicMethods = true,
|
hookAllPublicMethods = true
|
||||||
excludeHookMethodNames = { "toString", "hashCode" },
|
|
||||||
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
|
|
||||||
)
|
)
|
||||||
private ISynchronizedLockHooker synchronizedLock;
|
private ISynchronizedLockHooker synchronizedLock;
|
||||||
|
|
||||||
@@ -129,9 +206,7 @@ class JankPointAutoGenerator {
|
|||||||
group = "main_block_check",
|
group = "main_block_check",
|
||||||
type = Type.PARK,
|
type = Type.PARK,
|
||||||
scope = Scope.ALL,
|
scope = Scope.ALL,
|
||||||
hookAllPublicMethods = true,
|
hookAllPublicMethods = true
|
||||||
excludeHookMethodNames = { "unpark", "getBlocker", "toString", "hashCode" },
|
|
||||||
excludeHookMethodDescs = { "(Ljava/lang/Thread;)V", "(Ljava/lang/Thread;)Ljava/lang/Object;", "()Ljava/lang/String;", "()I"}
|
|
||||||
)
|
)
|
||||||
private LockSupport park;
|
private LockSupport park;
|
||||||
|
|
||||||
@@ -165,9 +240,44 @@ class JankPointAutoGenerator {
|
|||||||
hookType = HookType.AROUND,
|
hookType = HookType.AROUND,
|
||||||
mayCreateSuper = true,
|
mayCreateSuper = true,
|
||||||
hookAllPublicMethods = true,
|
hookAllPublicMethods = true,
|
||||||
excludeHookMethodNames = { "toString", "hashCode" },
|
onlyHookMethodNames = {
|
||||||
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
|
"onAttach",
|
||||||
|
"onCreate",
|
||||||
|
"onCreateView",
|
||||||
|
"onViewCreated",
|
||||||
|
"onActivityCreated",
|
||||||
|
"onViewStateRestored",
|
||||||
|
"onStart",
|
||||||
|
"onResume",
|
||||||
|
"onSaveInstanceState",
|
||||||
|
"onConfigurationChanged",
|
||||||
|
"onPause",
|
||||||
|
"onStop",
|
||||||
|
"onLowMemory",
|
||||||
|
"onTrimMemory",
|
||||||
|
"onDestroyView",
|
||||||
|
"onDestroy",
|
||||||
|
"onDetach",
|
||||||
|
},
|
||||||
|
onlyHookMethodDescs = {
|
||||||
|
"(Landroid/content/Context;)V",
|
||||||
|
"(Landroid/os/Bundle;)",
|
||||||
|
"(Landroid/view/LayoutInflater;Landroid/view/ViewGroup;Landroid/os/Bundle;)Landroid/view/View;",
|
||||||
|
"(Landroid/view/View;Landroid/os/Bundle;)V",
|
||||||
|
"(Landroid/os/Bundle;)V",
|
||||||
|
"(Landroid/os/Bundle;)V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
"(Landroid/os/Bundle;)V",
|
||||||
|
"(Landroid/content/res/Configuration;)V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
"(I)V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
private Fragment systemFragment;
|
private Fragment systemFragment;
|
||||||
|
|
||||||
@@ -178,8 +288,42 @@ class JankPointAutoGenerator {
|
|||||||
hookType = HookType.AROUND,
|
hookType = HookType.AROUND,
|
||||||
mayCreateSuper = true,
|
mayCreateSuper = true,
|
||||||
hookAllPublicMethods = true,
|
hookAllPublicMethods = true,
|
||||||
excludeHookMethodNames = { "toString", "hashCode" },
|
onlyHookMethodNames = {
|
||||||
excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" }
|
"onAttach",
|
||||||
|
"onCreate",
|
||||||
|
"onCreateView",
|
||||||
|
"onViewCreated",
|
||||||
|
"onActivityCreated",
|
||||||
|
"onViewStateRestored",
|
||||||
|
"onStart",
|
||||||
|
"onResume",
|
||||||
|
"onSaveInstanceState",
|
||||||
|
"onConfigurationChanged",
|
||||||
|
"onPause",
|
||||||
|
"onStop",
|
||||||
|
"onLowMemory",
|
||||||
|
"onDestroyView",
|
||||||
|
"onDestroy",
|
||||||
|
"onDetach",
|
||||||
|
},
|
||||||
|
onlyHookMethodDescs = {
|
||||||
|
"(Landroid/content/Context;)V",
|
||||||
|
"(Landroid/os/Bundle;)",
|
||||||
|
"(Landroid/view/LayoutInflater;Landroid/view/ViewGroup;Landroid/os/Bundle;)Landroid/view/View;",
|
||||||
|
"(Landroid/view/View;Landroid/os/Bundle;)V",
|
||||||
|
"(Landroid/os/Bundle;)V",
|
||||||
|
"(Landroid/os/Bundle;)V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
"(Landroid/os/Bundle;)V",
|
||||||
|
"(Landroid/content/res/Configuration;)V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
"()V",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
private androidx.fragment.app.Fragment androidxFragment;
|
private androidx.fragment.app.Fragment androidxFragment;
|
||||||
|
|
||||||
@@ -234,4 +378,20 @@ class JankPointAutoGenerator {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
private ViewManager manager;
|
private ViewManager manager;
|
||||||
|
|
||||||
|
|
||||||
|
@LancetXGenerator(
|
||||||
|
group = "main_block_check",
|
||||||
|
type = Type.OTHER,
|
||||||
|
scope = Scope.LEAF,
|
||||||
|
mayCreateSuper = true,
|
||||||
|
hookType = HookType.AROUND,
|
||||||
|
onlyHookMethodNames = {
|
||||||
|
"onDrawFrame"
|
||||||
|
},
|
||||||
|
onlyHookMethodDescs = {
|
||||||
|
"(Ljavax/microedition/khronos/opengles/GL10;)V"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
private GLSurfaceView.Renderer renderer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
package com.mogo.launcher.lancet.jank.spi
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.os.Message
|
||||||
|
import android.os.SystemClock
|
||||||
|
import com.google.auto.service.AutoService
|
||||||
|
import com.mogo.eagle.core.block.runtime.config.recorder.IMessageRecorder
|
||||||
|
import com.mogo.eagle.core.block.runtime.utils.TimeUtils.Companion.now
|
||||||
|
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider
|
||||||
|
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager
|
||||||
|
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager.block
|
||||||
|
import com.mogo.eagle.core.function.main.ARouterUtils
|
||||||
|
import com.mogo.eagle.core.handler.loader.IHandlerHooker
|
||||||
|
import com.mogo.eagle.core.block.runtime.message.Message as Msg
|
||||||
|
|
||||||
|
@AutoService(IHandlerHooker::class)
|
||||||
|
class HandlerHookerImpl : IHandlerHooker {
|
||||||
|
|
||||||
|
private val mainLooper = Looper.getMainLooper()
|
||||||
|
|
||||||
|
private var recorder: IMessageRecorder? = null
|
||||||
|
|
||||||
|
private var usage: IMoGoCpuUsageProvider? = null
|
||||||
|
|
||||||
|
private var dispatchTime = 0L
|
||||||
|
|
||||||
|
override fun onMessageDispatchAfter(msg: Message) {
|
||||||
|
if (msg.target.looper != mainLooper) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (dispatchTime <= 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val duration = now() - dispatchTime
|
||||||
|
checkIfNeed(msg.target)?.recycle(Msg.acquire(msg.target, msg = msg, duration = duration, elapsed = SystemClock.uptimeMillis() - msg.`when`, enqueue = msg.`when`))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMessageDispatchBefore(msg: Message) {
|
||||||
|
if (msg.target.looper != mainLooper) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dispatchTime = now()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRemoveCallbacks(handler: Handler, action: Runnable) {
|
||||||
|
checkIfNeed(handler)?.remove(Msg.acquire(handler, action = action))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRemoveCallbacksAndMessages(handler: Handler, token: Any?) {
|
||||||
|
checkIfNeed(handler)?.remove(Msg.acquire(handler, obj = token))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRemoveCallbacksWithToken(handler: Handler, action: Runnable, token: Any?) {
|
||||||
|
checkIfNeed(handler)?.remove(Msg.acquire(handler, action = action, obj = token))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRemoveMessages(handler: Handler, what: Int) {
|
||||||
|
checkIfNeed(handler)?.remove(Msg.acquire(handler, what = what))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRemoveMessages(handler: Handler, what: Int, obj: Any?) {
|
||||||
|
checkIfNeed(handler)?.remove(Msg.acquire(handler, what = what, obj = obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPost(handler: Handler, action: Runnable) {
|
||||||
|
checkIfNeed(handler)?.insert(Msg.acquire(handler, action = action))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostAtFrontQueue(handler: Handler, action: Runnable) {
|
||||||
|
checkIfNeed(handler)?.insert(Msg.acquire(handler, action = action))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostAtTime(handler: Handler, action: Runnable, token: Any?, uptimeMillis: Long) {
|
||||||
|
checkIfNeed(handler)?.insert(Msg.acquire(handler, action = action, obj = token, delay = uptimeMillis - SystemClock.uptimeMillis()))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostAtTime(handler: Handler, action: Runnable, uptimeMillis: Long) {
|
||||||
|
checkIfNeed(handler)?.insert(Msg.acquire(handler, action = action, delay = uptimeMillis - SystemClock.uptimeMillis()))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostDelayed(handler: Handler, action: Runnable, token: Any?, delayMillis: Long) {
|
||||||
|
checkIfNeed(handler)?.insert(Msg.acquire(handler, action = action, obj = token, delay = delayMillis))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostDelayed(handler: Handler, action: Runnable, delayMillis: Long) {
|
||||||
|
checkIfNeed(handler)?.insert(Msg.acquire(handler, action = action, delay = delayMillis))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onSendEmptyMessage(handler: Handler, what: Int) {
|
||||||
|
checkIfNeed(handler)?.insert(Msg.acquire(handler, what = what))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSendEmptyMessageAtTime(handler: Handler, what: Int, uptimeMillis: Long) {
|
||||||
|
checkIfNeed(handler)?.insert(Msg.acquire(handler, what = what, delay = uptimeMillis - SystemClock.uptimeMillis()))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSendEmptyMessageDelayed(handler: Handler, what: Int, delayMillis: Long) {
|
||||||
|
checkIfNeed(handler)?.insert(Msg.acquire(handler, what = what, delay = delayMillis))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSendMessage(handler: Handler, msg: Message) {
|
||||||
|
checkIfNeed(handler)?.insert(Msg.acquire(handler, msg, what = msg.what))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSendMessageAtFrontOfQueue(handler: Handler, msg: Message) {
|
||||||
|
checkIfNeed(handler)?.insert(Msg.acquire(handler, msg, what = msg.what))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSendMessageAtTime(handler: Handler, msg: Message, uptimeMillis: Long) {
|
||||||
|
checkIfNeed(handler)?.insert(Msg.acquire(handler, msg = msg, delay = uptimeMillis - SystemClock.uptimeMillis()))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSendMessageDelayed(handler: Handler, msg: Message, delayMillis: Long) {
|
||||||
|
checkIfNeed(handler)?.insert(Msg.acquire(handler, msg = msg, delay = delayMillis))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkIfNeed(handler: Handler?): IMessageRecorder? {
|
||||||
|
var usage = this.usage
|
||||||
|
if (usage == null && ARouterUtils.isInit.get()) {
|
||||||
|
usage = CallerDevaToolsManager.usage()
|
||||||
|
this.usage = usage
|
||||||
|
}
|
||||||
|
if (handler != null) {
|
||||||
|
if (handler.looper != mainLooper) {
|
||||||
|
usage?.updateOtherThreadTime()
|
||||||
|
} else {
|
||||||
|
usage?.updateMainThreadTime()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (handler != null && handler.looper != mainLooper) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (recorder == null) {
|
||||||
|
val hasInit = ARouterUtils.isInit.get()
|
||||||
|
if (hasInit && recorder == null) {
|
||||||
|
val block = block()
|
||||||
|
if (block != null && block.hasInit()) {
|
||||||
|
recorder = block.recorder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return recorder
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
package com.mogo.launcher.lancet.jank.spi;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.util.Log;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import com.google.auto.service.AutoService;
|
|
||||||
import com.mogo.core.lancetx.compiler.lib.generator.Type;
|
|
||||||
import com.mogo.core.lancetx.compiler.lib.hook.IHookInvoker;
|
|
||||||
import com.mogo.eagle.core.data.deva.chain.ChainConstant;
|
|
||||||
import com.zhjt.service.chain.ChainLog;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
|
|
||||||
@AutoService(IHookInvoker.class)
|
|
||||||
public class HookHandler implements IHookInvoker {
|
|
||||||
|
|
||||||
private final Looper mainLooper = Looper.getMainLooper();
|
|
||||||
|
|
||||||
|
|
||||||
private final AtomicLong startTime = new AtomicLong(0L);
|
|
||||||
|
|
||||||
private final AtomicLong syncLockStartTime = new AtomicLong(0L);
|
|
||||||
|
|
||||||
private final ThreadLocal<StringBuilder> message = new ThreadLocal<>();
|
|
||||||
|
|
||||||
private final ThreadLocal<Object> monitor = new ThreadLocal<>();
|
|
||||||
|
|
||||||
private final AtomicReference<Thread> holder = new AtomicReference<>();
|
|
||||||
|
|
||||||
private String holderDesc = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void i(@NonNull Type type, Object caller, @NonNull String methodName, @NonNull Object... objects) {
|
|
||||||
if (Looper.myLooper() == mainLooper) {
|
|
||||||
if (type == Type.SYNCHRONIZED_LOCK) {
|
|
||||||
if ("onMonitorBefore".equals(methodName)) {
|
|
||||||
syncLockStartTime.set(SystemClock.elapsedRealtime());
|
|
||||||
if (objects.length > 0) {
|
|
||||||
this.monitor.set(objects[0]);
|
|
||||||
}
|
|
||||||
Thread holder = this.holder.get();
|
|
||||||
if (holder != null) {
|
|
||||||
holderDesc = holder.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ("onMonitorEnter".equals(methodName)) {
|
|
||||||
long elapsedTime = SystemClock.elapsedRealtime() - syncLockStartTime.get();
|
|
||||||
if (elapsedTime >= 10) {
|
|
||||||
StringBuilder builder = message.get();
|
|
||||||
if (builder == null) {
|
|
||||||
builder = new StringBuilder();
|
|
||||||
message.set(builder);
|
|
||||||
}
|
|
||||||
if (builder.length() > 0) {
|
|
||||||
builder.setLength(0);
|
|
||||||
}
|
|
||||||
builder
|
|
||||||
.append(type)
|
|
||||||
.append("#")
|
|
||||||
.append(caller == null ? "caller is null" : caller.getClass().getSimpleName())
|
|
||||||
.append("#")
|
|
||||||
.append(methodName);
|
|
||||||
if (objects.length > 0) {
|
|
||||||
builder.append("#(");
|
|
||||||
}
|
|
||||||
for (Object o : objects) {
|
|
||||||
if (o == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
builder.append(o.getClass().getSimpleName())
|
|
||||||
.append(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objects.length > 0) {
|
|
||||||
builder.setLength(builder.length() - 1);
|
|
||||||
builder.append(")");
|
|
||||||
}
|
|
||||||
builder.append("#").append(elapsedTime);
|
|
||||||
|
|
||||||
Object monitor = this.monitor.get();
|
|
||||||
if (monitor != null) {
|
|
||||||
builder.append("#").append(monitor);
|
|
||||||
}
|
|
||||||
if (holderDesc != null) {
|
|
||||||
builder.append("#").append(holderDesc);
|
|
||||||
}
|
|
||||||
builder.append("\n");
|
|
||||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
|
||||||
for (StackTraceElement trace: stackTrace) {
|
|
||||||
builder.append(trace.getClassName()).append("#").append(trace.getMethodName()).append("#").append(trace.getLineNumber()).append("\n");
|
|
||||||
}
|
|
||||||
builder.setLength(builder.length() - 1);
|
|
||||||
Log.w("HookHandler", "Jank detected:" + builder);
|
|
||||||
linkedLog(type, builder.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ("onMonitorExit".equals(methodName)) {
|
|
||||||
this.monitor.remove();
|
|
||||||
holderDesc = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
startTime.set(SystemClock.elapsedRealtime());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (type == Type.SYNCHRONIZED_LOCK) {
|
|
||||||
if ("onMonitorBefore".equals(methodName)) {
|
|
||||||
if (objects.length > 0) {
|
|
||||||
this.monitor.set(objects[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("onMonitorEnter".equals(methodName)) {
|
|
||||||
holder.set(Thread.currentThread());
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("onMonitorExit".equals(methodName)) {
|
|
||||||
this.monitor.remove();
|
|
||||||
holder.set(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void o(@NonNull Type type, Object caller, @NonNull String methodName, @NonNull Object... objects) {
|
|
||||||
if (type != Type.SYNCHRONIZED_LOCK && Looper.myLooper() == mainLooper) {
|
|
||||||
long startTime = this.startTime.get();
|
|
||||||
if (startTime > 0) {
|
|
||||||
long delta = SystemClock.elapsedRealtime() - startTime;
|
|
||||||
if (delta >= 10) {
|
|
||||||
StringBuilder builder = message.get();
|
|
||||||
if (builder == null) {
|
|
||||||
builder = new StringBuilder();
|
|
||||||
message.set(builder);
|
|
||||||
}
|
|
||||||
if (builder.length() > 0) {
|
|
||||||
builder.setLength(0);
|
|
||||||
}
|
|
||||||
builder
|
|
||||||
.append(type)
|
|
||||||
.append("#")
|
|
||||||
.append(caller == null ? "caller is null" : caller.getClass().getSimpleName())
|
|
||||||
.append("#")
|
|
||||||
.append(methodName);
|
|
||||||
if (objects.length > 0) {
|
|
||||||
builder.append("#(");
|
|
||||||
}
|
|
||||||
for (Object o : objects) {
|
|
||||||
if (o == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
builder.append(o.getClass().getSimpleName())
|
|
||||||
.append(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objects.length > 0) {
|
|
||||||
builder.setLength(builder.length() - 1);
|
|
||||||
builder.append(")");
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.append("#").append(delta);
|
|
||||||
if (type == Type.AQS_LOCK) {
|
|
||||||
builder.append("\n");
|
|
||||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
|
||||||
for (StackTraceElement trace: stackTrace) {
|
|
||||||
builder.append(trace.getClassName()).append("#").append(trace.getMethodName()).append("#").append(trace.getLineNumber()).append("\n");
|
|
||||||
}
|
|
||||||
builder.setLength(builder.length() - 1);
|
|
||||||
}
|
|
||||||
Log.w("HookHandler", "Jank Detected:" + builder);
|
|
||||||
|
|
||||||
linkedLog(type, builder.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void linkedLog(Type type, String msg) {
|
|
||||||
try {
|
|
||||||
linkedLogInternal(type,msg);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
t.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ChainLog(
|
|
||||||
linkChainLog = ChainConstant.CHAIN_TYPE_ANR_LEAK,
|
|
||||||
linkCode = ChainConstant.CHAIN_SOURCE_HMI,
|
|
||||||
nodeAliasCode = ChainConstant.CHAIN_CODE_MAIN_BLOCK,
|
|
||||||
paramIndexes = { 0, 1 }
|
|
||||||
)
|
|
||||||
private void linkedLogInternal(Type type, String msg) {}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
package com.mogo.launcher.lancet.jank.spi;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.util.Log;
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import com.mogo.core.lancetx.compiler.lib.generator.Type;
|
||||||
|
import com.mogo.core.lancetx.compiler.lib.hook.IHookInvoker;
|
||||||
|
import com.mogo.eagle.core.data.deva.chain.ChainConstant;
|
||||||
|
import com.mogo.eagle.core.function.api.devatools.block.IMoGoBlockProvider;
|
||||||
|
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider;
|
||||||
|
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager;
|
||||||
|
import com.mogo.eagle.core.function.main.ARouterUtils;
|
||||||
|
import com.zhjt.service.chain.ChainLog;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
|
||||||
|
@AutoService(IHookInvoker.class)
|
||||||
|
public class HookInvokerImpl implements IHookInvoker {
|
||||||
|
|
||||||
|
private final Looper mainLooper = Looper.getMainLooper();
|
||||||
|
|
||||||
|
private final ThreadLocal<Long> startTime = new ThreadLocal<>();
|
||||||
|
private final AtomicLong syncLockStartTime = new AtomicLong(0L);
|
||||||
|
|
||||||
|
private final ThreadLocal<StringBuilder> message = new ThreadLocal<>();
|
||||||
|
|
||||||
|
private final ThreadLocal<Object> monitor = new ThreadLocal<>();
|
||||||
|
|
||||||
|
private final AtomicReference<Thread> holder = new AtomicReference<>();
|
||||||
|
|
||||||
|
private String holderDesc = null;
|
||||||
|
|
||||||
|
private volatile IMoGoCpuUsageProvider provider;
|
||||||
|
|
||||||
|
private volatile boolean getProviderRequested = false;
|
||||||
|
|
||||||
|
private final long logThreshold = 5; //日志打印阈值
|
||||||
|
|
||||||
|
private final long dumpStackThreshold = 20; // dump堆栈阈值
|
||||||
|
|
||||||
|
|
||||||
|
// 切记: 请勿在此方法中调用其它模块类中的api,可能会出现StackOverFlowException
|
||||||
|
@Override
|
||||||
|
public void i(Type type, Object caller,String methodName, Object... objects) {
|
||||||
|
startTime.set(SystemClock.elapsedRealtime());
|
||||||
|
boolean isMainThread = false;
|
||||||
|
if (mainLooper == Looper.myLooper()) {
|
||||||
|
isMainThread = true;
|
||||||
|
if (type == Type.SYNCHRONIZED_LOCK) {
|
||||||
|
handleSynchronizedLock(Looper.myLooper() == mainLooper, caller, methodName, objects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!getProviderRequested && provider == null && mainLooper != Looper.myLooper() && ARouterUtils.isInit.get()) {
|
||||||
|
getProviderRequested = true;
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
provider = CallerDevaToolsManager.INSTANCE.usage();
|
||||||
|
} finally {
|
||||||
|
getProviderRequested = false;
|
||||||
|
}
|
||||||
|
}, "getProvider").start();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (provider != null) {
|
||||||
|
if (isMainThread) {
|
||||||
|
provider.updateMainThreadTime();
|
||||||
|
} else {
|
||||||
|
provider.updateOtherThreadTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切记: 请勿在此方法中调用其它模块类中的api,可能会出现StackOverFlowException
|
||||||
|
@Override
|
||||||
|
public void o(Type type, Object caller,String methodName, Object... objects) {
|
||||||
|
long now = SystemClock.elapsedRealtime();
|
||||||
|
Long old = startTime.get();
|
||||||
|
long cost = now - (old == null ? now : old);
|
||||||
|
if (mainLooper == Looper.myLooper() && type != Type.SYNCHRONIZED_LOCK) {
|
||||||
|
handleCostTimeRecord(type, caller, methodName, cost, objects);
|
||||||
|
}
|
||||||
|
if (mainLooper == Looper.myLooper() && type == Type.ACTIVITY) {
|
||||||
|
handleActivity((Activity) caller, methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (provider != null) {
|
||||||
|
if (mainLooper == Looper.myLooper()) {
|
||||||
|
provider.updateMainThreadTime();
|
||||||
|
} else {
|
||||||
|
provider.updateOtherThreadTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCostTimeRecord(Type type, Object caller, String methodName, long cost, Object... args) {
|
||||||
|
if (cost >= logThreshold) {
|
||||||
|
StringBuilder builder = message.get();
|
||||||
|
if (builder == null) {
|
||||||
|
builder = new StringBuilder();
|
||||||
|
message.set(builder);
|
||||||
|
}
|
||||||
|
if (builder.length() > 0) {
|
||||||
|
builder.setLength(0);
|
||||||
|
}
|
||||||
|
builder
|
||||||
|
.append(type)
|
||||||
|
.append("#")
|
||||||
|
.append(caller == null ? "caller is null" : caller.getClass().getSimpleName())
|
||||||
|
.append("#")
|
||||||
|
.append(methodName);
|
||||||
|
if (args.length > 0) {
|
||||||
|
builder.append("#(");
|
||||||
|
}
|
||||||
|
for (Object o : args) {
|
||||||
|
if (o == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
builder.append(o.getClass().getSimpleName())
|
||||||
|
.append(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length > 0) {
|
||||||
|
builder.setLength(builder.length() - 1);
|
||||||
|
builder.append(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append("#").append(cost);
|
||||||
|
if (cost >= dumpStackThreshold) {
|
||||||
|
builder.append("\n");
|
||||||
|
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||||
|
for (StackTraceElement trace: stackTrace) {
|
||||||
|
builder.append(trace.getClassName()).append("#").append(trace.getMethodName()).append("#").append(trace.getLineNumber()).append("\n");
|
||||||
|
}
|
||||||
|
builder.setLength(builder.length() - 1);
|
||||||
|
}
|
||||||
|
Log.w("HookHandler", "Jank Detected:" + builder);
|
||||||
|
linkedLog(type, builder.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void handleActivity(Activity caller, String methodName) {
|
||||||
|
if ("onCreate".equals(methodName)) {
|
||||||
|
IMoGoBlockProvider block = CallerDevaToolsManager.INSTANCE.block();
|
||||||
|
if (block != null && block.hasInit()) {
|
||||||
|
block.monitor(caller.getWindow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ("onResume".equals(methodName)) {
|
||||||
|
IMoGoBlockProvider block = CallerDevaToolsManager.INSTANCE.block();
|
||||||
|
if (block != null && block.hasInit()) {
|
||||||
|
block.resume(caller.getWindow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("onPause".equals(methodName)) {
|
||||||
|
IMoGoBlockProvider block = CallerDevaToolsManager.INSTANCE.block();
|
||||||
|
if (block != null && block.hasInit()) {
|
||||||
|
block.pause(caller.getWindow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("onDestroy".equals(methodName)) {
|
||||||
|
IMoGoBlockProvider block = CallerDevaToolsManager.INSTANCE.block();
|
||||||
|
if (block != null && block.hasInit()) {
|
||||||
|
block.pop(caller.getWindow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleSynchronizedLock(boolean isMainThread,Object caller, String methodName, Object [] objects) {
|
||||||
|
if (isMainThread) {
|
||||||
|
if ("onMonitorBefore".equals(methodName)) {
|
||||||
|
syncLockStartTime.set(SystemClock.elapsedRealtime());
|
||||||
|
if (objects.length > 0) {
|
||||||
|
this.monitor.set(objects[0]);
|
||||||
|
}
|
||||||
|
Thread holder = this.holder.get();
|
||||||
|
if (holder != null) {
|
||||||
|
holderDesc = holder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ("onMonitorEnter".equals(methodName)) {
|
||||||
|
long elapsedTime = SystemClock.elapsedRealtime() - syncLockStartTime.get();
|
||||||
|
if (elapsedTime >= logThreshold) {
|
||||||
|
StringBuilder builder = message.get();
|
||||||
|
if (builder == null) {
|
||||||
|
builder = new StringBuilder();
|
||||||
|
message.set(builder);
|
||||||
|
}
|
||||||
|
if (builder.length() > 0) {
|
||||||
|
builder.setLength(0);
|
||||||
|
}
|
||||||
|
builder
|
||||||
|
.append(Type.SYNCHRONIZED_LOCK)
|
||||||
|
.append("#")
|
||||||
|
.append(caller == null ? "caller is null" : caller.getClass().getSimpleName())
|
||||||
|
.append("#")
|
||||||
|
.append(methodName);
|
||||||
|
if (objects.length > 0) {
|
||||||
|
builder.append("#(");
|
||||||
|
}
|
||||||
|
for (Object o : objects) {
|
||||||
|
if (o == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
builder.append(o.getClass().getSimpleName())
|
||||||
|
.append(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objects.length > 0) {
|
||||||
|
builder.setLength(builder.length() - 1);
|
||||||
|
builder.append(")");
|
||||||
|
}
|
||||||
|
builder.append("#").append(elapsedTime);
|
||||||
|
|
||||||
|
Object monitor = this.monitor.get();
|
||||||
|
if (monitor != null) {
|
||||||
|
builder.append("#").append(monitor);
|
||||||
|
}
|
||||||
|
if (holderDesc != null) {
|
||||||
|
builder.append("#").append(holderDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elapsedTime >= dumpStackThreshold) {
|
||||||
|
builder.append("\n");
|
||||||
|
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||||
|
for (StackTraceElement trace: stackTrace) {
|
||||||
|
builder.append(trace.getClassName()).append("#").append(trace.getMethodName()).append("#").append(trace.getLineNumber()).append("\n");
|
||||||
|
}
|
||||||
|
builder.setLength(builder.length() - 1);
|
||||||
|
}
|
||||||
|
Log.w("HookHandler", "Jank detected:" + builder);
|
||||||
|
linkedLog(Type.SYNCHRONIZED_LOCK, builder.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ("onMonitorExit".equals(methodName)) {
|
||||||
|
this.monitor.remove();
|
||||||
|
holderDesc = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ("onMonitorBefore".equals(methodName)) {
|
||||||
|
if (objects.length > 0) {
|
||||||
|
this.monitor.set(objects[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("onMonitorEnter".equals(methodName)) {
|
||||||
|
holder.set(Thread.currentThread());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("onMonitorExit".equals(methodName)) {
|
||||||
|
this.monitor.remove();
|
||||||
|
holder.set(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void linkedLog(Type type, String msg) {
|
||||||
|
try {
|
||||||
|
linkedLogInternal(type,msg);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ChainLog(
|
||||||
|
linkChainLog = ChainConstant.CHAIN_TYPE_ANR_LEAK,
|
||||||
|
linkCode = ChainConstant.CHAIN_SOURCE_HMI,
|
||||||
|
nodeAliasCode = ChainConstant.CHAIN_CODE_MAIN_BLOCK,
|
||||||
|
paramIndexes = { 0, 1 }
|
||||||
|
)
|
||||||
|
private void linkedLogInternal(Type type, String msg) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package com.mogo.launcher.lancet.jank.ui
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.View
|
||||||
|
import com.knightboost.lancet.api.Scope
|
||||||
|
import com.knightboost.lancet.api.annotations.Group
|
||||||
|
import com.knightboost.lancet.api.annotations.ReplaceInvoke
|
||||||
|
import com.knightboost.lancet.api.annotations.TargetClass
|
||||||
|
import com.knightboost.lancet.api.annotations.TargetMethod
|
||||||
|
import com.knightboost.lancet.api.annotations.Weaver
|
||||||
|
import com.mogo.eagle.core.block.runtime.message.Message
|
||||||
|
import com.mogo.eagle.core.block.runtime.utils.TimeUtils.Companion.now
|
||||||
|
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
@Weaver
|
||||||
|
@Group("main_block_check")
|
||||||
|
class UiPostLancet {
|
||||||
|
|
||||||
|
|
||||||
|
internal data class ActionWrapper(var action: Runnable): Runnable {
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
val start = now()
|
||||||
|
try {
|
||||||
|
action.run()
|
||||||
|
} finally {
|
||||||
|
CallerDevaToolsManager.block()?.takeIf {
|
||||||
|
it.hasInit()
|
||||||
|
}?.recorder()?.recycle(Message.acquire(handler = null, action = action, duration = now() - start))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val map by lazy { ConcurrentHashMap<Runnable, ActionWrapper>() }
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@TargetClass(value = "android.app.Activity", scope = Scope.ALL)
|
||||||
|
@TargetMethod(methodName = "runOnUiThread")
|
||||||
|
@ReplaceInvoke
|
||||||
|
fun runOnUiThreadOfActivity(activity: Activity, action: Runnable) {
|
||||||
|
try {
|
||||||
|
activity.runOnUiThread(ActionWrapper(action).also { map[action] = it })
|
||||||
|
} finally {
|
||||||
|
CallerDevaToolsManager.block()?.takeIf {
|
||||||
|
it.hasInit()
|
||||||
|
}?.recorder()?.insert(Message.acquire(handler = null, action = action, extra = mapOf(activity.javaClass.name to "runOnUiThread")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@TargetClass(value = "android.view.View", scope = Scope.ALL)
|
||||||
|
@TargetMethod(methodName = "post")
|
||||||
|
@ReplaceInvoke
|
||||||
|
fun post(view: View, action: Runnable): Boolean {
|
||||||
|
return try {
|
||||||
|
view.post(ActionWrapper(action).also { map[action] = it })
|
||||||
|
} finally {
|
||||||
|
CallerDevaToolsManager.block()?.takeIf {
|
||||||
|
it.hasInit()
|
||||||
|
}?.recorder()?.insert(Message.acquire(handler = null, action = action, extra = mapOf(view.javaClass.name to "post")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@TargetClass(value = "android.view.View", scope = Scope.ALL)
|
||||||
|
@TargetMethod(methodName = "postDelayed")
|
||||||
|
@ReplaceInvoke
|
||||||
|
fun postDelayed(view: View, action: Runnable, delayMillis: Long): Boolean {
|
||||||
|
return try {
|
||||||
|
view.postDelayed(action, delayMillis)
|
||||||
|
} finally {
|
||||||
|
CallerDevaToolsManager.block()?.takeIf {
|
||||||
|
it.hasInit()
|
||||||
|
}?.recorder()?.insert(Message.acquire(handler = null, action = action, extra = mapOf(view.javaClass.name to "post")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@TargetClass(value = "android.view.View", scope = Scope.ALL)
|
||||||
|
@TargetMethod(methodName = "removeCallbacks")
|
||||||
|
@ReplaceInvoke
|
||||||
|
fun removeCallbacks(view: View, action: Runnable): Boolean {
|
||||||
|
return try {
|
||||||
|
view.removeCallbacks(map.remove(action))
|
||||||
|
} finally {
|
||||||
|
CallerDevaToolsManager.block()?.takeIf {
|
||||||
|
it.hasInit()
|
||||||
|
}?.recorder()?.remove(Message.acquire(handler = null, action = action, extra = mapOf(view.javaClass.name to "removeCallbacks")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,12 +3,14 @@ package com.mogo.launcher.startup
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import com.alibaba.android.arouter.launcher.ARouter
|
import com.alibaba.android.arouter.launcher.ARouter
|
||||||
import com.mogo.commons.debug.DebugConfig
|
import com.mogo.commons.debug.DebugConfig
|
||||||
import com.mogo.eagle.core.function.main.threadopt.ThreadOptInitializer
|
import com.mogo.eagle.core.function.main.ARouterUtils
|
||||||
import com.mogo.eagle.core.utilcode.util.AppUtils
|
import com.mogo.eagle.core.utilcode.util.AppUtils
|
||||||
import com.mogo.eagle.core.utilcode.util.CleanUtils
|
import com.mogo.eagle.core.utilcode.util.CleanUtils
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
|
|
||||||
object ARouterStartUp {
|
object ARouterStartUp {
|
||||||
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun init(app: Application) {
|
fun init(app: Application) {
|
||||||
try {
|
try {
|
||||||
@@ -18,7 +20,7 @@ object ARouterStartUp {
|
|||||||
}
|
}
|
||||||
// 初始化 arouter
|
// 初始化 arouter
|
||||||
ARouter.init(app)
|
ARouter.init(app)
|
||||||
ThreadOptInitializer.aRouterInit.set(true)
|
ARouterUtils.isInit.set(true)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
// 由于ARouter会在SP_AROUTER_CACHE.xml缓存路由表,如果出现了被删除的情况会报错,这里清除下就好了
|
// 由于ARouter会在SP_AROUTER_CACHE.xml缓存路由表,如果出现了被删除的情况会报错,这里清除下就好了
|
||||||
|
|||||||
@@ -34,10 +34,11 @@ buildscript {
|
|||||||
classpath "com.mogo.cloud:systrace:${plugin_version}"
|
classpath "com.mogo.cloud:systrace:${plugin_version}"
|
||||||
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.18'
|
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.18'
|
||||||
classpath "com.mogo.sticky:service:${plugin_version}"
|
classpath "com.mogo.sticky:service:${plugin_version}"
|
||||||
classpath "io.github.knight-zxw:lancet-plugin:10.50.0"
|
classpath "io.github.knight-zxw:lancet-plugin:10.60.0"
|
||||||
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.15.0"
|
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.15.0"
|
||||||
classpath 'com.mogo.cloud:matrix:1.0.2'
|
classpath 'com.mogo.cloud:matrix:1.0.2'
|
||||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513'
|
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513'
|
||||||
|
classpath 'com.mogo.eagle.core.handler.proxy:plugin:1.0.0'
|
||||||
// classpath 'com.bytedance.btrace:rhea-gradle-plugin:2.0.0'
|
// classpath 'com.bytedance.btrace:rhea-gradle-plugin:2.0.0'
|
||||||
}
|
}
|
||||||
// 遇无法更新依赖情况(针对Snapshot无法刷新)然后sync project即可,刷新完成注释该代码
|
// 遇无法更新依赖情况(针对Snapshot无法刷新)然后sync project即可,刷新完成注释该代码
|
||||||
|
|||||||
@@ -209,9 +209,9 @@ ext {
|
|||||||
//========================= LancetX ===================
|
//========================= LancetX ===================
|
||||||
lancetx_runtime : "io.github.knight-zxw:lancet-runtime:10.50.0",
|
lancetx_runtime : "io.github.knight-zxw:lancet-runtime:10.50.0",
|
||||||
|
|
||||||
lancetx_compiler : "com.mogo.eagle.core.lancetx:compiler:1.0.0",
|
lancetx_compiler : "com.mogo.eagle.core.lancetx:compiler:1.0.5",
|
||||||
|
|
||||||
lancetx_compiler_lib : "com.mogo.eagle.core.lancetx:runtime:1.0.0",
|
lancetx_compiler_lib : "com.mogo.eagle.core.lancetx:runtime:1.0.5",
|
||||||
|
|
||||||
//========================= autosize ======================
|
//========================= autosize ======================
|
||||||
androidautoSize : 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1',
|
androidautoSize : 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1',
|
||||||
@@ -228,10 +228,13 @@ ext {
|
|||||||
passport_secret : "com.zhidaoauto:sdk-java:1.0.5-SNAPSHOT",
|
passport_secret : "com.zhidaoauto:sdk-java:1.0.5-SNAPSHOT",
|
||||||
|
|
||||||
// 主线程卡顿监测
|
// 主线程卡顿监测
|
||||||
block_detector : "com.mogo.eagle.core.block:runtime:10.50.0",
|
block_detector : "com.mogo.eagle.core.block:runtime:10.60.0",
|
||||||
|
|
||||||
//======================== google auto-service ===============
|
//======================== google auto-service ===============
|
||||||
google_auto_service : "com.google.auto.service:auto-service:1.0-rc7"
|
google_auto_service : "com.google.auto.service:auto-service:1.0-rc7",
|
||||||
|
|
||||||
|
//======================== handler-proxy-runtime ==============
|
||||||
|
handler_proxy_runtime : "com.mogo.eagle.core.handler.proxy:runtime:1.0.0"
|
||||||
]
|
]
|
||||||
android = [
|
android = [
|
||||||
fLauncherApplicationId : "com.mogo.launcher.f",
|
fLauncherApplicationId : "com.mogo.launcher.f",
|
||||||
|
|||||||
@@ -52,13 +52,13 @@ internal class MoGoBlockProviderImpl: IMoGoBlockProvider, IBlockListener {
|
|||||||
map["pending"] = data[1] ?: emptyList()
|
map["pending"] = data[1] ?: emptyList()
|
||||||
val cpu = CallerDevaToolsManager.usage()?.dump()
|
val cpu = CallerDevaToolsManager.usage()?.dump()
|
||||||
if (!cpu.isNullOrEmpty()) {
|
if (!cpu.isNullOrEmpty()) {
|
||||||
val mainThreadCpuUsage = cpu.remove("MainThreadCpuUsage")
|
val mainThreadUsage = cpu.remove("MainThreadUsage")
|
||||||
val processLaunchedTime = cpu.remove("ProcessLaunchedTime")
|
val processUsage = cpu.remove("ProcessUsage")
|
||||||
if (mainThreadCpuUsage != null && processLaunchedTime != null) {
|
if (mainThreadUsage != null && processUsage != null) {
|
||||||
linkedLog.recordCpuUsage(LinkedHashMap<String, String>().also {
|
linkedLog.recordCpuUsage(LinkedHashMap<String, String>().also { itx ->
|
||||||
it["MainThread"] = "${ mainThreadCpuUsage * 1.0f * 100 / processLaunchedTime }%"
|
itx["MainThread"] = "${ "%.2f".format(mainThreadUsage * 1.0f * 100 / processUsage) }% ($mainThreadUsage, $processUsage)"
|
||||||
cpu.entries.sortedByDescending { it.value }.forEach { sorted ->
|
cpu.entries.sortedByDescending { it.value }.forEach { sorted ->
|
||||||
it[sorted.key] = "${ sorted.value * 1.0f * 100 / processLaunchedTime }%"
|
itx[sorted.key] = "${ "%.2f".format(sorted.value * 1.0f * 100 / processUsage) }% (${sorted.value}, $processUsage)"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.zhjt.mogo_core_function_devatools.perf
|
package com.zhjt.mogo_core_function_devatools.perf
|
||||||
|
|
||||||
|
import android.os.Debug
|
||||||
|
import android.os.Looper
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider
|
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
@@ -11,53 +13,67 @@ internal class MoGoCpuUsageProviderImpl: IMoGoCpuUsageProvider {
|
|||||||
|
|
||||||
private var mainThreadStartTime : Long = 0L
|
private var mainThreadStartTime : Long = 0L
|
||||||
|
|
||||||
private var mainThreadLastTime: Long = 0L
|
|
||||||
|
|
||||||
private var mainThreadCpuUsage: Long = 0L
|
private var mainThreadCpuUsage: Long = 0L
|
||||||
|
|
||||||
private val maxSize = 500
|
private val maxSize = 500
|
||||||
|
|
||||||
private val map by lazy { LinkedHashMap<String, Long>(0, 0.75f, true) }
|
private val map = LinkedHashMap<String, Long>(0, 0.75f, true)
|
||||||
|
|
||||||
private val lock by lazy { ReentrantLock() }
|
private val lock = ReentrantLock()
|
||||||
|
|
||||||
private val builder by lazy { ThreadLocal<StringBuilder>() }
|
private val builder = ThreadLocal<StringBuilder>()
|
||||||
|
|
||||||
override fun onProcessLaunched(time: Long) {
|
private val otherThreadLastTime = ThreadLocal<Long>()
|
||||||
processLaunchTime = time
|
|
||||||
|
private val mainLooper = Looper.getMainLooper()
|
||||||
|
|
||||||
|
override fun onProcessLaunched() {
|
||||||
|
processLaunchTime = SystemClock.elapsedRealtime()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun startMainThreadTime(time: Long) {
|
override fun onMainThreadLaunched() {
|
||||||
mainThreadStartTime = time
|
mainThreadStartTime = Debug.threadCpuTimeNanos() / 1000_000
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateMainThreadTime(time: Long) {
|
override fun updateMainThreadTime() {
|
||||||
if (mainThreadLastTime == 0L) {
|
if (Looper.myLooper() != mainLooper) {
|
||||||
mainThreadLastTime = mainThreadStartTime
|
return
|
||||||
}
|
}
|
||||||
val delta = time - mainThreadLastTime
|
if (mainThreadStartTime > 0) {
|
||||||
if (delta > 0) {
|
mainThreadCpuUsage = (Debug.threadCpuTimeNanos() / 1000_000 - mainThreadStartTime)
|
||||||
mainThreadCpuUsage += delta
|
|
||||||
}
|
}
|
||||||
mainThreadLastTime = time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun incrementOtherThreadUsage(group: String, t: Thread, usage: Long) {
|
override fun updateOtherThreadTime() {
|
||||||
var builder = builder.get()
|
var last = otherThreadLastTime.get()
|
||||||
if (builder == null) {
|
val now = Debug.threadCpuTimeNanos() / 1000_000
|
||||||
builder = StringBuilder(128)
|
try {
|
||||||
this.builder.set(builder)
|
if (last == null) {
|
||||||
}
|
last = now
|
||||||
if (builder.isNotEmpty()) {
|
otherThreadLastTime.set(last)
|
||||||
builder.setLength(0)
|
|
||||||
}
|
|
||||||
builder.append(group).append("@@").append(t.name)
|
|
||||||
lock.withLock {
|
|
||||||
while (map.size > maxSize) {
|
|
||||||
val toEvict = map.entries.iterator().next()
|
|
||||||
map.remove(toEvict.key)
|
|
||||||
}
|
}
|
||||||
map[builder.toString()] = map.getOrPut(builder.toString()) { 0 } + usage
|
val duration = now - last
|
||||||
|
if (duration < 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var builder = builder.get()
|
||||||
|
if (builder == null) {
|
||||||
|
builder = StringBuilder(128)
|
||||||
|
this.builder.set(builder)
|
||||||
|
}
|
||||||
|
if (builder.isNotEmpty()) {
|
||||||
|
builder.setLength(0)
|
||||||
|
}
|
||||||
|
builder.append(Thread.currentThread().name)
|
||||||
|
lock.withLock {
|
||||||
|
while (map.size > maxSize) {
|
||||||
|
val toEvict = map.entries.iterator().next()
|
||||||
|
map.remove(toEvict.key)
|
||||||
|
}
|
||||||
|
map[builder.toString()] = map.getOrPut(builder.toString()) { 0 } + duration
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
otherThreadLastTime.set(now)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,8 +82,8 @@ internal class MoGoCpuUsageProviderImpl: IMoGoCpuUsageProvider {
|
|||||||
if (map.isNotEmpty()) {
|
if (map.isNotEmpty()) {
|
||||||
val iterator = map.entries.iterator()
|
val iterator = map.entries.iterator()
|
||||||
val rst = LinkedHashMap<String, Long>()
|
val rst = LinkedHashMap<String, Long>()
|
||||||
rst["MainThreadCpuUsage"] = mainThreadCpuUsage
|
rst["MainThreadUsage"] = mainThreadCpuUsage
|
||||||
rst["ProcessLaunchedTime"] = SystemClock.elapsedRealtimeNanos() - processLaunchTime
|
rst["ProcessUsage"] = SystemClock.elapsedRealtime() - processLaunchTime
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
val next = iterator.next()
|
val next = iterator.next()
|
||||||
rst[next.key] = next.value
|
rst[next.key] = next.value
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.mogo.eagle.core.function.main
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
|
class ARouterUtils {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
var isInit = AtomicBoolean(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +1,23 @@
|
|||||||
package com.mogo.eagle.core.function.main.threadopt;
|
package com.mogo.eagle.core.function.main.threadopt;
|
||||||
|
|
||||||
import android.os.Debug;
|
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.SystemClock;
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.os.HandlerCompat;
|
import androidx.core.os.HandlerCompat;
|
||||||
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider;
|
import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider;
|
||||||
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager;
|
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager;
|
||||||
|
import com.mogo.eagle.core.function.main.ARouterUtils;
|
||||||
import com.mogo.thread.opt.core.ThreadManager;
|
import com.mogo.thread.opt.core.ThreadManager;
|
||||||
import com.mogo.thread.opt.core.annotation.Keep;
|
import com.mogo.thread.opt.core.annotation.Keep;
|
||||||
import com.mogo.thread.opt.core.config.ThreadConfig;
|
import com.mogo.thread.opt.core.config.ThreadConfig;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
public class ThreadOptInitializer {
|
public class ThreadOptInitializer {
|
||||||
|
|
||||||
public static final AtomicBoolean aRouterInit = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
|
|
||||||
private static final ThreadConfig.TaskExecuteListener listener = new ThreadConfig.TaskExecuteListener() {
|
private static final ThreadConfig.TaskExecuteListener listener = new ThreadConfig.TaskExecuteListener() {
|
||||||
|
|
||||||
@@ -38,29 +35,29 @@ public class ThreadOptInitializer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onExecutorBefore(@NonNull ThreadConfig.TaskType taskType, @Nullable Object task, @Nullable String desc) {
|
public void onExecutorBefore(@NonNull ThreadConfig.TaskType taskType, @Nullable Object task, @Nullable String desc) {
|
||||||
if (!recorded && aRouterInit.get()) {
|
if (!recorded && ARouterUtils.isInit.get()) {
|
||||||
IMoGoCpuUsageProvider usage = CallerDevaToolsManager.INSTANCE.usage();
|
IMoGoCpuUsageProvider usage = CallerDevaToolsManager.INSTANCE.usage();
|
||||||
if (usage != null) {
|
if (usage != null) {
|
||||||
recorded = true;
|
recorded = true;
|
||||||
HandlerCompat.createAsync(Looper.getMainLooper()).post(() -> usage.startMainThreadTime(Debug.threadCpuTimeNanos()));
|
HandlerCompat.createAsync(Looper.getMainLooper()).post(usage::onMainThreadLaunched);
|
||||||
usage.onProcessLaunched(SystemClock.elapsedRealtimeNanos());
|
usage.onProcessLaunched();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recorded) {
|
if (recorded) {
|
||||||
start.set(Debug.threadCpuTimeNanos());
|
IMoGoCpuUsageProvider usage = CallerDevaToolsManager.INSTANCE.usage();
|
||||||
|
if (usage != null) {
|
||||||
|
usage.updateOtherThreadTime();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onExecutorAfter(@NonNull ThreadConfig.TaskType taskType, @Nullable Object task, @Nullable String desc) {
|
public void onExecutorAfter(@NonNull ThreadConfig.TaskType taskType, @Nullable Object task, @Nullable String desc) {
|
||||||
if (recorded) {
|
if (recorded) {
|
||||||
Long start = this.start.get();
|
IMoGoCpuUsageProvider usage = CallerDevaToolsManager.INSTANCE.usage();
|
||||||
if (start != null && start > 0) {
|
if (usage != null) {
|
||||||
long delta = Debug.threadCpuTimeNanos() - start;
|
usage.updateOtherThreadTime();
|
||||||
IMoGoCpuUsageProvider usage = CallerDevaToolsManager.INSTANCE.usage();
|
|
||||||
if (usage != null && desc != null) {
|
|
||||||
usage.incrementOtherThreadUsage(desc, Thread.currentThread(), delta);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ package com.mogo.eagle.core.function.api.devatools.perf
|
|||||||
|
|
||||||
interface IMoGoCpuUsageProvider {
|
interface IMoGoCpuUsageProvider {
|
||||||
|
|
||||||
fun onProcessLaunched(time: Long)
|
fun onProcessLaunched()
|
||||||
|
|
||||||
fun startMainThreadTime(time: Long)
|
fun onMainThreadLaunched()
|
||||||
|
|
||||||
fun updateMainThreadTime(time: Long)
|
fun updateMainThreadTime()
|
||||||
|
|
||||||
fun incrementOtherThreadUsage(group: String, t: Thread, usage: Long)
|
fun updateOtherThreadTime()
|
||||||
|
|
||||||
fun dump(): LinkedHashMap<String, Long>
|
fun dump(): LinkedHashMap<String, Long>
|
||||||
}
|
}
|
||||||
@@ -17,5 +17,6 @@ if (!isAndroidTest) {
|
|||||||
apply from: rootProject.file('gradle/bytex/bytex_apm.gradle')
|
apply from: rootProject.file('gradle/bytex/bytex_apm.gradle')
|
||||||
apply from: rootProject.file('gradle/bytex/bytex_systrace.gradle')
|
apply from: rootProject.file('gradle/bytex/bytex_systrace.gradle')
|
||||||
apply from: rootProject.file('gradle/bytex/bytex_matrix.gradle')
|
apply from: rootProject.file('gradle/bytex/bytex_matrix.gradle')
|
||||||
|
apply from: rootProject.file('gradle/bytex/bytex_handler_proxy.gradle')
|
||||||
// apply from: rootProject.file('gradle/bytex/bytex_btrace.gradle')
|
// apply from: rootProject.file('gradle/bytex/bytex_btrace.gradle')
|
||||||
}
|
}
|
||||||
|
|||||||
6
gradle/bytex/bytex_handler_proxy.gradle
Normal file
6
gradle/bytex/bytex_handler_proxy.gradle
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
apply plugin: 'bytex.handler_proxy'
|
||||||
|
handler_proxy {
|
||||||
|
enable true
|
||||||
|
enableInDebug true
|
||||||
|
}
|
||||||
|
|
||||||
@@ -2,8 +2,13 @@ apply plugin: 'LancetX'
|
|||||||
LancetX {
|
LancetX {
|
||||||
enable true
|
enable true
|
||||||
enableInDebug true
|
enableInDebug true
|
||||||
|
blackList = [
|
||||||
|
"com.mogo.launcher.lancet.jank",
|
||||||
|
"com.mogo.thread.opt.core",
|
||||||
|
"com.zhjt.mogo_core_function_devatools.perf"
|
||||||
|
]
|
||||||
synchronizedLock {
|
synchronizedLock {
|
||||||
enabled false
|
enabled true
|
||||||
blackList = [
|
blackList = [
|
||||||
"okio.AsyncTimeout\$Watchdog"
|
"okio.AsyncTimeout\$Watchdog"
|
||||||
]
|
]
|
||||||
@@ -23,7 +28,7 @@ LancetX {
|
|||||||
enable true
|
enable true
|
||||||
}
|
}
|
||||||
main_block_check {
|
main_block_check {
|
||||||
enable false
|
enable true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user