Android 获取应用使用时长统计数据

系统分析

Posted by JovenHe on August 18, 2022

项目中有需要统计应用的使用时长,每天0点获取前一天的应用使用时长。

使用了UsageStatsManager.queryUsageStats来获取,之前测试是正常的,但真正使用中却多次获取错误,当天未使用的应用也给统计出来了,而且时长和华为系统中的健康使用多出两倍或三倍

错误原因有人曾分析过

关于 UsageStatsManager.queryUsageStats 的注意事项及 UsageStatsService 的简单原理

文中提到,queryUsageStats 接口并不是按照传入的开始结束时间戳来计算的,而是受时区影响,所以如果使用2号的0点和24点时间戳传入,会获取到1号的数据,导致数据重复问题

需要改为使用queryEvents来计算,该方法返回Event数据包含开始结束时间内的所有使用过应用的activity的MOVE_TO_FOREGROUND、MOVE_TO_BACKGROUND 、USER_INTERACTION 等事件,可以拿来计算,但处理过程比较麻烦,可以直接反编译系统设置中统计相关逻辑

Screenshot_20220818_141623_com.huawei.parentcontrol.jpg

该页面为com.huawei.parentcontrol/com.huawei.parentcontrol.ui.activity.AppUsageDetailActivity

直接导出parentcontrol对应apk,使用jadx打开,查找AppUsageDetailActivity,对应AppUsageFragment

AppUsageFragment.png

很容易找到关键的折线图相关,以此可以找到折线图的数据获取

queryEvents.png

把相关代码提取出来

object AppUsageStatHelper {

    fun getAppUseTimeSevenDay(context: Context?, str: String): Map<String?, Long?>? {
        val str2 = "AppUsageStatHelper"
        if (context == null || TextUtils.isEmpty(str)) {

            return HashMap(0)
        }
        val a: Long = m8244a(1)
        val currentTimeMillis = System.currentTimeMillis()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
            val usageStatsManager =
                context.getSystemService(AppCompatActivity.USAGE_STATS_SERVICE) as UsageStatsManager
            if (usageStatsManager != null) {
                val queryEvents: UsageEvents =
                    usageStatsManager.queryEvents(a - 14400000, currentTimeMillis)
                if (queryEvents == null) {
                    return HashMap(0)
                }
                val a2: Map<String?, Long?> = m6684a(
                    str,
                    queryEvents,
                    a,
                    currentTimeMillis,
                    "今天"
                )
                return a2
            }
        }
        return null

    }
    fun m8244a(i: Int): Long {
        if (i == 0) {
            return TimeUtils.getWeeOfToday()
        }
        if (i != 1) {
            return 0
        }
        val a: Long = TimeUtils.m8328a(6)
        return a
    }

    /* renamed from: a */
    private fun m6684a(
        str: String,
        usageEvents: UsageEvents?,
        j: Long,
        j2: Long,
        str2: String
    ): Map<String?, Long?> {
        val event = UsageEvents.Event()
        val sevenDayUsageData = SevenDayUsageData(str, j, j2, str2)
        if (usageEvents != null) {
            while (usageEvents.getNextEvent(event)) {
                if (str == event.getPackageName()) {
                    if (m6689a(event)) {
                        sevenDayUsageData.mo7089a(event)
                    }
                }
            }
        }
        sevenDayUsageData.mo11419c()
        return sevenDayUsageData.mo11420d()
    }
    private fun m6689a(event: UsageEvents.Event?): Boolean {
        return if (event == null) {
            false
        } else if (Build.VERSION.SDK_INT < 29 || event.getEventType() !== 23) {
            val eventType: Int = event.getEventType()
            if (eventType == 1 || eventType == 2) {
                true
            } else false
        } else {
            if ("com.huawei.himovie" == event.getPackageName()) {
                false
            } else true
        }
    }

    internal class SevenDayUsageData(
        str: String?, j: Long, j2: Long, /* renamed from: l */
        private val f8542l: String
    ) :
        BaseUsageData(str!!, j, j2) {
        /* renamed from: j */
        private val f8540j: MutableMap<String?, Long?> = HashMap()

        /* renamed from: k */
        private val f8541k = LongArray(7)

        /* renamed from: a */
        private fun m12496a(j: Long, j2: Long, i: Int) {
            val obj: String
            if (mo7091a(j, this.f4753c)) {
                obj = f8542l
            } else {
                obj = TimeUtils.m8333a(java.lang.Long.valueOf(j))
            }
            var j3: Long = 86400000
            if (f8540j.containsKey(obj)) {
                val longValue = f8540j[obj]!!.toLong() + j2
                if (longValue <= 86400000) {
                    j3 = longValue
                }
                f8540j[obj] = java.lang.Long.valueOf(j3)
            } else if (j2 > 86400000) {

                return
            } else {
                f8540j[obj] = java.lang.Long.valueOf(j2)
            }
            m12495a(m12497e(j), j2)
        }

        /* access modifiers changed from: protected */ /* renamed from: a */
        override fun mo7088a(): String {
            return "SevenDayUsageData"
        }

        /* access modifiers changed from: protected */ /* renamed from: c */
        override fun mo7095c(j: Long) {
            val j2 = this.f4754d
            val j3 = this.f4755e
            this.f4754d = j2 + (j - j3)
            if (mo7091a(j3, j)) {
                m12496a(j, j - this.f4755e, 1)
                return
            }
            val f = m12498f(j)
            val j4 = this.f4755e
            m12496a(j4, f - j4, 2)
            m12496a(j, j - f, 3)
        }

        /* renamed from: d */
        fun mo11420d(): Map<String?, Long?> {
            return f8540j
        }

        /* renamed from: e */
        fun mo11421e(): LongArray {
            return f8541k.clone()
        }

        /* renamed from: f */
        fun mo11422f(): List<Long> {
            val arrayList = ArrayList<Long>(7)
            for (valueOf in f8541k) {
                arrayList.add(java.lang.Long.valueOf(valueOf))
            }
            return arrayList
        }

        /* renamed from: e */
        private fun m12497e(j: Long): Int {
            return 6 - mo7087a(j)
        }

        /* renamed from: f */
        private fun m12498f(j: Long): Long {
            val instance = Calendar.getInstance()
            instance.timeInMillis = j
            instance[11] = 0
            instance[12] = 0
            instance[13] = 0
            return instance.timeInMillis
        }

        /* renamed from: c */
        fun mo11419c() {
            if (this.f4756f == 1) {
                val currentTimeMillis = System.currentTimeMillis()
                mo7095c(currentTimeMillis)
                this.f4755e = currentTimeMillis
            }
        }

        /* renamed from: a */
        private fun m12495a(i: Int, j: Long) {
            val str = "SevenDayUsageData"
            if (i >= 0) {
                val jArr = f8541k
                if (i < jArr.size) {
                    if (j < 0 || j > 86400000) {
                        return
                    } else {
                        jArr[i] = jArr[i] + j
                        return
                    }
                }
            }
        }
    }
    abstract class BaseUsageData(/* renamed from: a */
        protected var f4751a: String, /* renamed from: b */
        protected var f4752b: Long, /* renamed from: c */
        protected var f4753c: Long
    ) {
        /* renamed from: d */
        protected var f4754d: Long = 0

        /* renamed from: e */
        protected var f4755e: Long = 0

        /* renamed from: f */
        protected var f4756f = 0

        /* renamed from: g */
        protected var f4757g: MutableMap<String, Int> = HashMap()

        /* renamed from: h */
        private val f4758h = Calendar.getInstance()

        /* renamed from: i */
        private val f4759i = Calendar.getInstance()

        /* renamed from: e */
        private fun m6472e(j: Long) {
            val j2 = this.f4755e
            if (j > j2) {
                this.f4755e = j
            } else if (j < j2) {
                val a = mo7088a()
            } else {
                val a2 = mo7088a()
            }
        }

        /* renamed from: f */
        private fun m6473f(j: Long) {
            val j2 = this.f4752b
            if (j >= j2) {
                if (this.f4755e < j2) {
                    this.f4755e = j2
                }
                mo7095c(j)
                this.f4755e = j
            }
        }

        /* renamed from: a */
        abstract fun mo7088a(): String

        /* renamed from: a */
        fun mo7089a(event: UsageEvents.Event) {
            if (mo7094b(event)) {
                val timeStamp: Long = event.getTimeStamp()
                val className: String = event.getClassName()
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
                    if (this.f4757g.getOrDefault(className, Integer.valueOf(0)).toInt() == 1) {
                        if (timeStamp > this.f4755e) {
                            m6473f(timeStamp)
                        } else {

                        }
                    }
                }

                val eventType: Int = event.getEventType()
                if (eventType == 1) {
                    m6472e(timeStamp)
                    this.f4757g[className] = Integer.valueOf(eventType)
                } else if (eventType == 2) {
                    this.f4757g[className] = Integer.valueOf(eventType)
                } else if (eventType != 23) {
                } else {
                    this.f4757g.remove(className)
                }
                this.f4756f = event.getEventType()
            }
        }

        /* access modifiers changed from: protected */ /* renamed from: a */
        fun mo7090a(i: Int): Boolean {
            return i == 1 || i == 2 || i == 23
        }

        /* renamed from: b */
        fun mo7093b(): Long {
            return this.f4754d
        }

        /* renamed from: c */
        abstract fun mo7095c(j: Long)

        /* access modifiers changed from: protected|final */ /* renamed from: d */
        fun mo7096d(j: Long): Long {
            this.f4758h.timeInMillis = j
            this.f4758h[11] = 0
            this.f4758h[12] = 0
            this.f4758h[13] = 0
            this.f4758h[14] = 0
            return this.f4758h.timeInMillis
        }

        /* access modifiers changed from: protected */ /* renamed from: b */
        fun mo7094b(event: UsageEvents.Event?): Boolean {
            if (event == null) {
                return false
            }
            val str = f4751a
            if (str == null || str != event.getPackageName()) {
                return false
            }
            val eventType: Int = event.getEventType()
            if (mo7090a(eventType)) {
                return true
            }

            return false
        }

        /* access modifiers changed from: protected|final */ /* renamed from: b */
        fun mo7092b(j: Long): Int {
            this.f4758h.timeInMillis = j
            return this.f4758h[11]
        }

        /* access modifiers changed from: protected|final */ /* renamed from: a */
        fun mo7091a(j: Long, j2: Long): Boolean {
            this.f4758h.timeInMillis = j
            f4759i.timeInMillis = j2
            return this.f4758h[6] === f4759i[6]
        }

        /* access modifiers changed from: protected|final */ /* renamed from: a */
        fun mo7087a(j: Long): Int {
            this.f4758h.timeInMillis = j
            f4759i.timeInMillis = this.f4753c
            return f4759i[6] - this.f4758h[6]
        }

        init {
            if (TextUtils.isEmpty(f4751a)) {

            }
        }
    }
}

调用

val appUsageStatsByTime = AppUsageStatHelper.getAppUseTimeSevenDay(
    mContext,
    "com.jingdong.app.mall"
)

结果为{“今天”:251654,”8/17”:571679}

转化为分钟,今天4分钟,昨天9分钟,与系统统计信息一致

Screenshot_20220818_164334_com.huawei.parentcontrol.jpg