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.content.Context
20 import android.net.NetworkStats
21 import android.net.NetworkTemplate
22 import android.telephony.SubscriptionManager
23 import android.telephony.TelephonyManager
24 import android.util.Log
25 
26 /**
27  * Lib class for data usage
28  */
29 object DataUsageLib {
30     private const val TAG = "DataUsageLib"
31 
32     /**
33      * Return mobile NetworkTemplate based on `subId`
34      */
35     @JvmStatic
getMobileTemplatenull36     fun getMobileTemplate(context: Context, subId: Int): NetworkTemplate {
37         val telephonyManager = context.getSystemService(TelephonyManager::class.java)!!
38         val mobileDefaultSubId = telephonyManager.subscriptionId
39         val subscriptionManager = context.getSystemService(SubscriptionManager::class.java)!!
40         val subInfoList = subscriptionManager.availableSubscriptionInfoList
41         if (subInfoList == null) {
42             Log.i(TAG, "Subscription is not inited: $subId")
43             return getMobileTemplateForSubId(telephonyManager, mobileDefaultSubId)
44         }
45         for (subInfo in subInfoList) {
46             if (subInfo?.subscriptionId == subId) {
47                 return getNormalizedMobileTemplate(telephonyManager, subId)
48             }
49         }
50         Log.i(TAG, "Subscription is not active: $subId")
51         return getMobileTemplateForSubId(telephonyManager, mobileDefaultSubId)
52     }
53 
getNormalizedMobileTemplatenull54     private fun getNormalizedMobileTemplate(
55         telephonyManager: TelephonyManager,
56         subId: Int,
57     ): NetworkTemplate {
58         val mobileTemplate = getMobileTemplateForSubId(telephonyManager, subId)
59         val mergedSubscriberIds =
60             telephonyManager.createForSubscriptionId(subId).mergedImsisFromGroup
61         if (mergedSubscriberIds.isNullOrEmpty()) {
62             Log.i(TAG, "mergedSubscriberIds is empty.")
63             return mobileTemplate
64         }
65         return normalizeMobileTemplate(mobileTemplate, mergedSubscriberIds)
66     }
67 
normalizeMobileTemplatenull68     private fun normalizeMobileTemplate(
69         template: NetworkTemplate,
70         merged: Array<String?>,
71     ): NetworkTemplate {
72         val subscriberId = template.subscriberIds.firstOrNull() ?: return template
73         // In some rare cases (e.g. b/243015487), merged subscriberId list might contain
74         // duplicated items. Deduplication for better error handling.
75         val mergedSet = merged.toSet()
76         if (mergedSet.size != merged.size) {
77             Log.wtf(TAG, "Duplicated merged list detected: " + merged.contentToString())
78         }
79         return if (mergedSet.contains(subscriberId)) {
80             // Requested template subscriber is part of the merge group; return
81             // a template that matches all merged subscribers.
82             NetworkTemplate.Builder(template.matchRule)
83                 .setSubscriberIds(mergedSet)
84                 .setMeteredness(template.meteredness)
85                 .build()
86         } else template
87     }
88 
89     @JvmStatic
getMobileTemplateForSubIdnull90     fun getMobileTemplateForSubId(telephonyManager: TelephonyManager, subId: Int): NetworkTemplate {
91         // Create template that matches any mobile network when the subscriberId is null.
92         val subscriberId = telephonyManager.getSubscriberId(subId)
93         return when (subscriberId) {
94             null -> NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
95             else -> NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
96                 .setSubscriberIds(setOf(subscriberId))
97         }.setMeteredness(NetworkStats.METERED_YES).build()
98     }
99 }
100