1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.datausage.lib
18 
19 import android.app.usage.NetworkStats
20 import android.app.usage.NetworkStatsManager
21 import android.content.Context
22 import android.net.NetworkTemplate
23 import android.util.Log
24 import android.util.Range
25 
26 class NetworkStatsRepository(context: Context, private val template: NetworkTemplate) {
27     private val networkStatsManager = context.getSystemService(NetworkStatsManager::class.java)!!
28 
queryAggregateForUidnull29     fun queryAggregateForUid(
30         range: Range<Long>,
31         uid: Int,
32         state: Int = NetworkStats.Bucket.STATE_ALL,
33     ): NetworkUsageData? = try {
34         networkStatsManager.queryDetailsForUidTagState(
35             template, range.lower, range.upper, uid, NetworkStats.Bucket.TAG_NONE, state,
36         ).convertToBuckets().aggregate()
37     } catch (e: Exception) {
38         Log.e(TAG, "Exception queryDetailsForUidTagState", e)
39         null
40     }
41 
queryDetailsForDevicenull42     fun queryDetailsForDevice(): List<Bucket> = try {
43         networkStatsManager.queryDetailsForDevice(template, Long.MIN_VALUE, Long.MAX_VALUE)
44             .convertToBuckets()
45     } catch (e: Exception) {
46         Log.e(TAG, "Exception queryDetailsForDevice", e)
47         emptyList()
48     }
49 
getTimeRangenull50     fun getTimeRange(): Range<Long>? = queryDetailsForDevice().aggregate()?.timeRange
51 
52     fun querySummaryForDevice(startTime: Long, endTime: Long): Long = try {
53         networkStatsManager.querySummaryForDevice(template, startTime, endTime).bytes
54     } catch (e: Exception) {
55         Log.e(TAG, "Exception querySummaryForDevice", e)
56         0
57     }
58 
queryBucketsnull59     fun queryBuckets(startTime: Long, endTime: Long): List<Bucket> = try {
60         networkStatsManager.querySummary(template, startTime, endTime).convertToBuckets()
61     } catch (e: Exception) {
62         Log.e(TAG, "Exception querySummary", e)
63         emptyList()
64     }
65 
66     companion object {
67         private const val TAG = "NetworkStatsRepository"
68 
69         val AllTimeRange = Range(Long.MIN_VALUE, Long.MAX_VALUE)
70 
71         data class Bucket(
72             val uid: Int,
73             val bytes: Long,
74             val state: Int = NetworkStats.Bucket.STATE_ALL,
75             val startTimeStamp: Long,
76             val endTimeStamp: Long,
77         )
78 
Listnull79         fun List<Bucket>.aggregate(): NetworkUsageData? = when {
80             isEmpty() -> null
81             else -> NetworkUsageData(
82                 startTime = minOf { it.startTimeStamp },
83                 endTime = maxOf { it.endTimeStamp },
84                 usage = sumOf { it.bytes },
85             )
86         }
87 
<lambda>null88         fun List<Bucket>.filterTime(startTime: Long, endTime: Long): List<Bucket> = filter {
89             it.startTimeStamp >= startTime && it.endTimeStamp <= endTime
90         }
91 
<lambda>null92         private fun NetworkStats.convertToBuckets(): List<Bucket> = use {
93             val buckets = mutableListOf<Bucket>()
94             val bucket = NetworkStats.Bucket()
95             while (getNextBucket(bucket)) {
96                 if (bucket.bytes > 0) {
97                     buckets += Bucket(
98                         uid = bucket.uid,
99                         bytes = bucket.bytes,
100                         state = bucket.state,
101                         startTimeStamp = bucket.startTimeStamp,
102                         endTimeStamp = bucket.endTimeStamp,
103                     )
104                 }
105             }
106             buckets
107         }
108 
109         private val NetworkStats.Bucket.bytes: Long
110             get() = rxBytes + txBytes
111     }
112 }
113