Merge "Convert DataUsageLib to Kotlin" into main

This commit is contained in:
Chaohui Wang
2023-08-08 05:05:01 +00:00
committed by Android (Google) Code Review
4 changed files with 270 additions and 231 deletions

View File

@@ -1,115 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datausage.lib;
import android.content.Context;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.internal.util.ArrayUtils;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
/**
* Lib class for data usage
*/
public class DataUsageLib {
private static final String TAG = "DataUsageLib";
/**
* Return mobile NetworkTemplate based on {@code subId}
*/
public static NetworkTemplate getMobileTemplate(Context context, int subId) {
final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
final int mobileDefaultSubId = telephonyManager.getSubscriptionId();
final SubscriptionManager subscriptionManager =
context.getSystemService(SubscriptionManager.class);
final List<SubscriptionInfo> subInfoList =
subscriptionManager.getAvailableSubscriptionInfoList();
if (subInfoList == null) {
Log.i(TAG, "Subscription is not inited: " + subId);
return getMobileTemplateForSubId(telephonyManager, mobileDefaultSubId);
}
for (SubscriptionInfo subInfo : subInfoList) {
if ((subInfo != null) && (subInfo.getSubscriptionId() == subId)) {
return getNormalizedMobileTemplate(telephonyManager, subId);
}
}
Log.i(TAG, "Subscription is not active: " + subId);
return getMobileTemplateForSubId(telephonyManager, mobileDefaultSubId);
}
private static NetworkTemplate getNormalizedMobileTemplate(
TelephonyManager telephonyManager, int subId) {
final NetworkTemplate mobileTemplate = getMobileTemplateForSubId(telephonyManager, subId);
final String[] mergedSubscriberIds = telephonyManager
.createForSubscriptionId(subId).getMergedImsisFromGroup();
if (ArrayUtils.isEmpty(mergedSubscriberIds)) {
Log.i(TAG, "mergedSubscriberIds is null.");
return mobileTemplate;
}
return normalizeMobileTemplate(mobileTemplate, mergedSubscriberIds);
}
private static NetworkTemplate normalizeMobileTemplate(
@NonNull NetworkTemplate template, @NonNull String[] merged) {
if (template.getSubscriberIds().isEmpty()) return template;
// The input template should have at most 1 subscriberId.
final String subscriberId = template.getSubscriberIds().iterator().next();
// In some rare cases (e.g. b/243015487), merged subscriberId list might contain
// duplicated items. Deduplication for better error handling.
final ArraySet mergedSet = new ArraySet(merged);
if (mergedSet.size() != merged.length) {
Log.wtf(TAG, "Duplicated merged list detected: " + Arrays.toString(merged));
}
if (mergedSet.contains(subscriberId)) {
// Requested template subscriber is part of the merge group; return
// a template that matches all merged subscribers.
return new NetworkTemplate.Builder(template.getMatchRule())
.setSubscriberIds(mergedSet)
.setMeteredness(template.getMeteredness()).build();
}
return template;
}
public static NetworkTemplate getMobileTemplateForSubId(
TelephonyManager telephonyManager, int subId) {
// Create template that matches any mobile network when the subscriberId is null.
String subscriberId = telephonyManager.getSubscriberId(subId);
return subscriberId != null
? new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
.setSubscriberIds(Set.of(subscriberId))
.setMeteredness(NetworkStats.METERED_YES)
.build()
: new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
.setMeteredness(NetworkStats.METERED_YES)
.build();
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datausage.lib
import android.content.Context
import android.net.NetworkStats
import android.net.NetworkTemplate
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.util.Log
/**
* Lib class for data usage
*/
object DataUsageLib {
private const val TAG = "DataUsageLib"
/**
* Return mobile NetworkTemplate based on `subId`
*/
@JvmStatic
fun getMobileTemplate(context: Context, subId: Int): NetworkTemplate {
val telephonyManager = context.getSystemService(TelephonyManager::class.java)!!
val mobileDefaultSubId = telephonyManager.subscriptionId
val subscriptionManager = context.getSystemService(SubscriptionManager::class.java)!!
val subInfoList = subscriptionManager.availableSubscriptionInfoList
if (subInfoList == null) {
Log.i(TAG, "Subscription is not inited: $subId")
return getMobileTemplateForSubId(telephonyManager, mobileDefaultSubId)
}
for (subInfo in subInfoList) {
if (subInfo?.subscriptionId == subId) {
return getNormalizedMobileTemplate(telephonyManager, subId)
}
}
Log.i(TAG, "Subscription is not active: $subId")
return getMobileTemplateForSubId(telephonyManager, mobileDefaultSubId)
}
private fun getNormalizedMobileTemplate(
telephonyManager: TelephonyManager,
subId: Int,
): NetworkTemplate {
val mobileTemplate = getMobileTemplateForSubId(telephonyManager, subId)
val mergedSubscriberIds =
telephonyManager.createForSubscriptionId(subId).mergedImsisFromGroup
if (mergedSubscriberIds.isNullOrEmpty()) {
Log.i(TAG, "mergedSubscriberIds is empty.")
return mobileTemplate
}
return normalizeMobileTemplate(mobileTemplate, mergedSubscriberIds)
}
private fun normalizeMobileTemplate(
template: NetworkTemplate,
merged: Array<String?>,
): NetworkTemplate {
val subscriberId = template.subscriberIds.firstOrNull() ?: return template
// In some rare cases (e.g. b/243015487), merged subscriberId list might contain
// duplicated items. Deduplication for better error handling.
val mergedSet = merged.toSet()
if (mergedSet.size != merged.size) {
Log.wtf(TAG, "Duplicated merged list detected: " + merged.contentToString())
}
return if (mergedSet.contains(subscriberId)) {
// Requested template subscriber is part of the merge group; return
// a template that matches all merged subscribers.
NetworkTemplate.Builder(template.matchRule)
.setSubscriberIds(mergedSet)
.setMeteredness(template.meteredness)
.build()
} else template
}
@JvmStatic
fun getMobileTemplateForSubId(telephonyManager: TelephonyManager, subId: Int): NetworkTemplate {
// Create template that matches any mobile network when the subscriberId is null.
val subscriberId = telephonyManager.getSubscriberId(subId)
return when (subscriberId) {
null -> NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
else -> NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
.setSubscriberIds(setOf(subscriberId))
}.setMeteredness(NetworkStats.METERED_YES).build()
}
}

View File

@@ -1,116 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datausage.lib;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.NetworkTemplate;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class DataUsageLibTest {
private static final int SUB_ID = 1;
private static final int SUB_ID_2 = 2;
private static final String SUBSCRIBER_ID = "Test Subscriber";
private static final String SUBSCRIBER_ID_2 = "Test Subscriber 2";
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private SubscriptionManager mSubscriptionManager;
@Mock
private SubscriptionInfo mInfo1;
@Mock
private SubscriptionInfo mInfo2;
@Mock
private ParcelUuid mParcelUuid;
private Context mContext;
private List<SubscriptionInfo> mInfos;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
when(mTelephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID);
when(mTelephonyManager.getSubscriberId(SUB_ID_2)).thenReturn(SUBSCRIBER_ID_2);
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
when(mSubscriptionManager.isActiveSubscriptionId(anyInt())).thenReturn(true);
}
@Test
@Ignore
public void getMobileTemplate_infoNull_returnMobileAll() {
when(mSubscriptionManager.isActiveSubscriptionId(SUB_ID)).thenReturn(false);
final NetworkTemplate networkTemplate = DataUsageLib.getMobileTemplate(mContext, SUB_ID);
assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID)).isTrue();
assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID_2)).isFalse();
}
@Test
@Ignore
public void getMobileTemplate_groupUuidNull_returnMobileAll() {
when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
when(mInfo1.getGroupUuid()).thenReturn(null);
when(mTelephonyManager.getMergedImsisFromGroup())
.thenReturn(new String[] {SUBSCRIBER_ID});
final NetworkTemplate networkTemplate = DataUsageLib.getMobileTemplate(mContext, SUB_ID);
assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID)).isTrue();
assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID_2)).isFalse();
}
@Test
@Ignore
public void getMobileTemplate_groupUuidExist_returnMobileMerged() {
when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
when(mInfo1.getGroupUuid()).thenReturn(mParcelUuid);
// In some rare cases (e.g. b/243015487), merged subscriberId list might contain
// duplicated items. The implementation should perform deduplication.
when(mTelephonyManager.getMergedImsisFromGroup())
.thenReturn(new String[] {SUBSCRIBER_ID, SUBSCRIBER_ID, SUBSCRIBER_ID_2});
final NetworkTemplate networkTemplate = DataUsageLib.getMobileTemplate(mContext, SUB_ID);
assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID)).isTrue();
assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID_2)).isTrue();
assertThat(networkTemplate.getSubscriberIds().size() == 2).isTrue();
}
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datausage.lib
import android.content.Context
import android.net.NetworkStats
import android.net.NetworkTemplate
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class DataUsageLibTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var telephonyManager: TelephonyManager
@Mock
private lateinit var subscriptionManager: SubscriptionManager
@Before
fun setUp() {
whenever(context.getSystemService(TelephonyManager::class.java))
.thenReturn(telephonyManager)
whenever(context.getSystemService(SubscriptionManager::class.java))
.thenReturn(subscriptionManager)
whenever(telephonyManager.subscriptionId).thenReturn(DEFAULT_SUB_ID)
whenever(telephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID)
whenever(telephonyManager.getSubscriberId(DEFAULT_SUB_ID)).thenReturn(DEFAULT_SUBSCRIBER_ID)
whenever(telephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(telephonyManager)
}
@Test
fun getMobileTemplate_availableSubscriptionInfoListIsNull_returnDefaultSub() {
whenever(subscriptionManager.availableSubscriptionInfoList).thenReturn(null)
val mobileTemplate = DataUsageLib.getMobileTemplate(context, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_CARRIER)
assertThat(mobileTemplate.subscriberIds).containsExactly(DEFAULT_SUBSCRIBER_ID)
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
@Test
fun getMobileTemplate_subscriptionNotActive_returnDefaultSub() {
whenever(subscriptionManager.availableSubscriptionInfoList).thenReturn(listOf(null))
val mobileTemplate = DataUsageLib.getMobileTemplate(context, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_CARRIER)
assertThat(mobileTemplate.subscriberIds).containsExactly(DEFAULT_SUBSCRIBER_ID)
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
@Test
fun getMobileTemplate_mergedImsisFromGroupNull_returnRequestedSub() {
whenever(subscriptionManager.availableSubscriptionInfoList)
.thenReturn(listOf(SUBSCRIBER_INFO))
whenever(telephonyManager.mergedImsisFromGroup).thenReturn(null)
val mobileTemplate = DataUsageLib.getMobileTemplate(context, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_CARRIER)
assertThat(mobileTemplate.subscriberIds).containsExactly(SUBSCRIBER_ID)
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
@Test
fun getMobileTemplate_mergedImsisFromGroupEmpty_returnRequestedSub() {
whenever(subscriptionManager.availableSubscriptionInfoList)
.thenReturn(listOf(SUBSCRIBER_INFO))
whenever(telephonyManager.mergedImsisFromGroup).thenReturn(emptyArray())
val mobileTemplate = DataUsageLib.getMobileTemplate(context, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_CARRIER)
assertThat(mobileTemplate.subscriberIds).containsExactly(SUBSCRIBER_ID)
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
@Test
fun getMobileTemplate_mergedImsisFromGroupNotContainSub_returnRequestedSub() {
whenever(subscriptionManager.availableSubscriptionInfoList)
.thenReturn(listOf(SUBSCRIBER_INFO))
whenever(telephonyManager.mergedImsisFromGroup).thenReturn(arrayOf(DEFAULT_SUBSCRIBER_ID))
val mobileTemplate = DataUsageLib.getMobileTemplate(context, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_CARRIER)
assertThat(mobileTemplate.subscriberIds).containsExactly(SUBSCRIBER_ID)
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
@Test
fun getMobileTemplate_mergedImsisFromGroupContainSub_returnRequestedSub() {
whenever(subscriptionManager.availableSubscriptionInfoList)
.thenReturn(listOf(SUBSCRIBER_INFO))
whenever(telephonyManager.mergedImsisFromGroup)
.thenReturn(arrayOf(DEFAULT_SUBSCRIBER_ID, SUBSCRIBER_ID))
val mobileTemplate = DataUsageLib.getMobileTemplate(context, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_CARRIER)
assertThat(mobileTemplate.subscriberIds)
.containsExactly(SUBSCRIBER_ID, DEFAULT_SUBSCRIBER_ID)
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
@Test
fun getMobileTemplateForSubId_subscriberIdNotNull() {
whenever(telephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID)
val mobileTemplate = DataUsageLib.getMobileTemplateForSubId(telephonyManager, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_CARRIER)
assertThat(mobileTemplate.subscriberIds).containsExactly(SUBSCRIBER_ID)
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
@Test
fun getMobileTemplateForSubId_subscriberIdIsNull() {
whenever(telephonyManager.getSubscriberId(SUB_ID)).thenReturn(null)
val mobileTemplate = DataUsageLib.getMobileTemplateForSubId(telephonyManager, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_MOBILE)
assertThat(mobileTemplate.subscriberIds).isEmpty()
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
private companion object {
const val DEFAULT_SUB_ID = 0
const val SUB_ID = 1
const val DEFAULT_SUBSCRIBER_ID = "Default Test Subscriber"
const val SUBSCRIBER_ID = "Test Subscriber"
val SUBSCRIBER_INFO: SubscriptionInfo = SubscriptionInfo.Builder().setId(SUB_ID).build()
}
}