Enable network type icon overlays by carrier Id
This CL builds out a new configuration resource `carrierid_icon_overrides`, and a format that allows for overriding a known network type with an arbitrary icon. We store a mapping of all known carrierId overrides that exist, and check for an override for the `carrierId & networkType` combination at runtime. This CL also caches the calculation in SystemUI since it represents a new expensive calculation potential at the time of display for the icon. Lastly, add demo mode command parsing for carrierId Test: atest MobileStateTest; atest NetworkControllerDataTest Test: manual using `-e carrierid <id>` network subcommand Bug: 241996752 Change-Id: Id0e8acf179db5c3a70f4caf31e58f8d8040690a0
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
<!--
|
||||
Copyright (C) 2022 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="17"
|
||||
android:width="22dp"
|
||||
android:height="17dp">
|
||||
<group>
|
||||
<group>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M1.03 8.47l0.43-4.96h4.33v1.17H2.48L2.25 7.39C2.66 7.1 3.1 6.96 3.57 6.96c0.77 0 1.38 0.3 1.83 0.9 s0.66 1.41 0.66 2.43c0 1.03-0.24 1.84-0.72 2.43S4.2 13.6 3.36 13.6c-0.75 0-1.36-0.24-1.83-0.73s-0.74-1.16-0.81-2.02h1.13 c0.07 0.57 0.23 1 0.49 1.29s0.59 0.43 1.01 0.43c0.47 0 0.84-0.2 1.1-0.61c0.26-0.41 0.4-0.96 0.4-1.65 c0-0.65-0.14-1.18-0.43-1.59S3.76 8.09 3.28 8.09c-0.4 0-0.72 0.1-0.96 0.31L1.99 8.73L1.03 8.47z"/>
|
||||
</group>
|
||||
<group>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M 18.93,5.74 L 18.93,3.39 L 17.63,3.39 L 17.63,5.74 L 15.28,5.74 L 15.28,7.04 L 17.63,7.04 L 17.63,9.39 L 18.93,9.39 L 18.93,7.04 L 21.28,7.04 L 21.28,5.74 z"/>
|
||||
</group>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M13.78 12.24l-0.22 0.27c-0.63 0.73-1.55 1.1-2.76 1.1c-1.08 0-1.92-0.36-2.53-1.07s-0.93-1.72-0.94-3.02V7.56 c0-1.39 0.28-2.44 0.84-3.13s1.39-1.04 2.51-1.04c0.95 0 1.69 0.26 2.23 0.79s0.83 1.28 0.89 2.26h-1.25 c-0.05-0.62-0.22-1.1-0.52-1.45s-0.74-0.52-1.34-0.52c-0.72 0-1.24 0.23-1.57 0.7S8.6 6.37 8.59 7.4v2.03c0 1 0.19 1.77 0.57 2.31 c0.38 0.54 0.93 0.8 1.65 0.8c0.67 0 1.19-0.16 1.54-0.49l0.18-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
|
||||
</group>
|
||||
</vector>
|
||||
32
packages/SettingsLib/res/values/carrierid_icon_overrides.xml
Normal file
32
packages/SettingsLib/res/values/carrierid_icon_overrides.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<!--
|
||||
~ This resource file exists to enumerate all network type icon overrides on a
|
||||
~ per-carrierId basis
|
||||
-->
|
||||
<resources>
|
||||
<!--
|
||||
Network type (RAT) icon overrides can be configured here on a per-carrierId basis.
|
||||
1. Add a new TypedArray here, using the naming scheme below
|
||||
2. The entries are (NetworkType, drawable ID) pairs
|
||||
3. Add this array's ID to the MAPPING field of MobileIconCarrierIdOverrides.kt
|
||||
-->
|
||||
<array name="carrierId_2032_iconOverrides">
|
||||
<item>5G_PLUS</item>
|
||||
<item>@drawable/ic_5g_plus_mobiledata_default</item>
|
||||
</array>
|
||||
</resources>
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.settingslib.mobile
|
||||
|
||||
import android.annotation.DrawableRes
|
||||
import android.content.res.Resources
|
||||
import android.content.res.TypedArray
|
||||
import android.util.Log
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.settingslib.R
|
||||
import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||
|
||||
/**
|
||||
* This class defines a network type (3G, 4G, etc.) override mechanism on a per-carrierId basis.
|
||||
*
|
||||
* Traditionally, carrier-customized network type iconography was achieved using the `MCC/MNC`
|
||||
* resource qualifiers, and swapping out the drawable resource by name. It would look like this:
|
||||
*
|
||||
* res/
|
||||
* drawable/
|
||||
* 3g_mobiledata_icon.xml
|
||||
* drawable-MCC-MNC/
|
||||
* 3g_mobiledata_icon.xml
|
||||
*
|
||||
* This would mean that, provided a context created with this MCC/MNC configuration set, loading
|
||||
* the network type icon through [MobileIconGroup] would provide a carrier-defined network type
|
||||
* icon rather than the AOSP-defined default.
|
||||
*
|
||||
* The MCC/MNC mechanism no longer can fully define carrier-specific network type icons, because
|
||||
* there is no longer a 1:1 mapping between MCC/MNC and carrier. With the advent of MVNOs, multiple
|
||||
* carriers can have the same MCC/MNC value, but wish to differentiate based on their carrier ID.
|
||||
* CarrierId is a newer concept than MCC/MNC, and provides more granularity when it comes to
|
||||
* determining the carrier (e.g. MVNOs can share MCC/MNC values with the network owner), therefore
|
||||
* it can fit all of the same use cases currently handled by `MCC/MNC`, without the need to apply a
|
||||
* configuration context in order to get the proper UI for a given SIM icon.
|
||||
*
|
||||
* NOTE: CarrierId icon overrides will always take precedence over those defined using `MCC/MNC`
|
||||
* resource qualifiers.
|
||||
*
|
||||
* [MAPPING] encodes the relationship between CarrierId and the corresponding override array
|
||||
* that exists in the config.xml. An alternative approach could be to generate the resource name
|
||||
* by string concatenation at run-time:
|
||||
*
|
||||
* val resName = "carrierId_$carrierId_iconOverrides"
|
||||
* val override = resources.getResourceIdentifier(resName)
|
||||
*
|
||||
* However, that's going to be far less efficient until MAPPING grows to a sufficient size. For now,
|
||||
* given a relatively small number of entries, we should just maintain the mapping here.
|
||||
*/
|
||||
interface MobileIconCarrierIdOverrides {
|
||||
@DrawableRes
|
||||
fun getOverrideFor(carrierId: Int, networkType: String, resources: Resources): Int
|
||||
fun carrierIdEntryExists(carrierId: Int): Boolean
|
||||
}
|
||||
|
||||
class MobileIconCarrierIdOverridesImpl : MobileIconCarrierIdOverrides {
|
||||
@DrawableRes
|
||||
override fun getOverrideFor(carrierId: Int, networkType: String, resources: Resources): Int {
|
||||
val resId = MAPPING[carrierId] ?: return 0
|
||||
val ta = resources.obtainTypedArray(resId)
|
||||
val map = parseNetworkIconOverrideTypedArray(ta)
|
||||
ta.recycle()
|
||||
return map[networkType] ?: 0
|
||||
}
|
||||
|
||||
override fun carrierIdEntryExists(carrierId: Int) =
|
||||
overrideExists(carrierId, MAPPING)
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MobileIconOverrides"
|
||||
/**
|
||||
* This map maintains the lookup from the canonical carrier ID (see below link) to the
|
||||
* corresponding overlay resource. New overrides should add an entry below in order to
|
||||
* change the network type icon resources based on carrier ID
|
||||
*
|
||||
* Refer to the link below for the canonical mapping maintained in AOSP:
|
||||
* https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb
|
||||
*/
|
||||
private val MAPPING = mapOf(
|
||||
// 2032 == Xfinity Mobile
|
||||
2032 to R.array.carrierId_2032_iconOverrides,
|
||||
)
|
||||
|
||||
/**
|
||||
* Parse `carrierId_XXXX_iconOverrides` for a particular network type. The resource file
|
||||
* "carrierid_icon_overrides.xml" defines a TypedArray format for overriding specific
|
||||
* network type icons (a.k.a. RAT icons) for a particular carrier ID. The format is defined
|
||||
* as an array of (network type name, drawable) pairs:
|
||||
* <array name="carrierId_XXXX_iconOverrides>
|
||||
* <item>NET_TYPE_1</item>
|
||||
* <item>@drawable/net_type_1_override</item>
|
||||
* <item>NET_TYPE_2</item>
|
||||
* <item>@drawable/net_type_2_override</item>
|
||||
* </array>
|
||||
*
|
||||
* @param ta the [TypedArray] defined in carrierid_icon_overrides.xml
|
||||
* @return the overridden drawable resource ID if it exists, or 0 if it does not
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@JvmStatic
|
||||
fun parseNetworkIconOverrideTypedArray(ta: TypedArray): Map<String, Int> {
|
||||
if (ta.length() % 2 != 0) {
|
||||
Log.w(TAG,
|
||||
"override must contain an even number of (key, value) entries. skipping")
|
||||
|
||||
return mapOf()
|
||||
}
|
||||
|
||||
val result = mutableMapOf<String, Int>()
|
||||
// The array is defined as Pair(String, resourceId), so walk by 2
|
||||
for (i in 0 until ta.length() step 2) {
|
||||
val key = ta.getString(i)
|
||||
val override = ta.getResourceId(i + 1, 0)
|
||||
if (key == null || override == 0) {
|
||||
Log.w(TAG, "Invalid override found. Skipping")
|
||||
continue
|
||||
}
|
||||
result[key] = override
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
private fun overrideExists(carrierId: Int, mapping: Map<Int, Int>): Boolean =
|
||||
mapping.containsKey(carrierId)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.settingslib.mobile;
|
||||
|
||||
import static com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl.parseNetworkIconOverrideTypedArray;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.res.TypedArray;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public final class MobileIconCarrierIdOverridesTest {
|
||||
private static final String OVERRIDE_ICON_1_NAME = "name_1";
|
||||
private static final int OVERRIDE_ICON_1_RES = 1;
|
||||
|
||||
private static final String OVERRIDE_ICON_2_NAME = "name_2";
|
||||
private static final int OVERRIDE_ICON_2_RES = 2;
|
||||
|
||||
NetworkOverrideTypedArrayMock mResourceMock;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mResourceMock = new NetworkOverrideTypedArrayMock(
|
||||
new String[] { OVERRIDE_ICON_1_NAME, OVERRIDE_ICON_2_NAME },
|
||||
new int[] { OVERRIDE_ICON_1_RES, OVERRIDE_ICON_2_RES }
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_singleOverride() {
|
||||
mResourceMock.setOverrides(
|
||||
new String[] { OVERRIDE_ICON_1_NAME },
|
||||
new int[] { OVERRIDE_ICON_1_RES }
|
||||
);
|
||||
|
||||
Map<String, Integer> parsed = parseNetworkIconOverrideTypedArray(mResourceMock.getMock());
|
||||
|
||||
assertThat(parsed.get(OVERRIDE_ICON_1_NAME)).isEqualTo(OVERRIDE_ICON_1_RES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_multipleOverrides() {
|
||||
mResourceMock.setOverrides(
|
||||
new String[] { OVERRIDE_ICON_1_NAME, OVERRIDE_ICON_2_NAME },
|
||||
new int[] { OVERRIDE_ICON_1_RES, OVERRIDE_ICON_2_RES }
|
||||
);
|
||||
|
||||
Map<String, Integer> parsed = parseNetworkIconOverrideTypedArray(mResourceMock.getMock());
|
||||
|
||||
assertThat(parsed.get(OVERRIDE_ICON_2_NAME)).isEqualTo(OVERRIDE_ICON_2_RES);
|
||||
assertThat(parsed.get(OVERRIDE_ICON_1_NAME)).isEqualTo(OVERRIDE_ICON_1_RES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_nonexistentKey_isNull() {
|
||||
mResourceMock.setOverrides(
|
||||
new String[] { OVERRIDE_ICON_1_NAME },
|
||||
new int[] { OVERRIDE_ICON_1_RES }
|
||||
);
|
||||
|
||||
Map<String, Integer> parsed = parseNetworkIconOverrideTypedArray(mResourceMock.getMock());
|
||||
|
||||
assertThat(parsed.get(OVERRIDE_ICON_2_NAME)).isNull();
|
||||
}
|
||||
|
||||
static class NetworkOverrideTypedArrayMock {
|
||||
private Object[] mInterleaved;
|
||||
|
||||
private final TypedArray mMockTypedArray = mock(TypedArray.class);
|
||||
|
||||
NetworkOverrideTypedArrayMock(
|
||||
String[] networkTypes,
|
||||
int[] iconOverrides) {
|
||||
|
||||
mInterleaved = interleaveTypes(networkTypes, iconOverrides);
|
||||
|
||||
doAnswer(invocation -> {
|
||||
return mInterleaved[(int) invocation.getArgument(0)];
|
||||
}).when(mMockTypedArray).getString(/* index */ anyInt());
|
||||
|
||||
doAnswer(invocation -> {
|
||||
return mInterleaved[(int) invocation.getArgument(0)];
|
||||
}).when(mMockTypedArray).getResourceId(/* index */ anyInt(), /* default */ anyInt());
|
||||
|
||||
when(mMockTypedArray.length()).thenAnswer(invocation -> {
|
||||
return mInterleaved.length;
|
||||
});
|
||||
}
|
||||
|
||||
TypedArray getMock() {
|
||||
return mMockTypedArray;
|
||||
}
|
||||
|
||||
void setOverrides(String[] types, int[] resIds) {
|
||||
mInterleaved = interleaveTypes(types, resIds);
|
||||
}
|
||||
|
||||
private Object[] interleaveTypes(String[] strs, int[] ints) {
|
||||
assertThat(strs.length).isEqualTo(ints.length);
|
||||
|
||||
Object[] ret = new Object[strs.length * 2];
|
||||
|
||||
// Keep track of where we are in the interleaved array, but iterate the overrides
|
||||
int interleavedIndex = 0;
|
||||
for (int i = 0; i < strs.length; i++) {
|
||||
ret[interleavedIndex] = strs[i];
|
||||
interleavedIndex += 1;
|
||||
ret[interleavedIndex] = ints[i];
|
||||
interleavedIndex += 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,7 @@
|
||||
*/
|
||||
package com.android.systemui.statusbar.connectivity;
|
||||
|
||||
import static com.android.settingslib.mobile.MobileMappings.getDefaultIcons;
|
||||
import static com.android.settingslib.mobile.MobileMappings.getIconKey;
|
||||
import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
|
||||
import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -46,6 +44,7 @@ import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults;
|
||||
import com.android.settingslib.mobile.TelephonyIcons;
|
||||
import com.android.settingslib.net.SignalStrengthUtil;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy;
|
||||
import com.android.systemui.util.CarrierConfigTracker;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -63,6 +62,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
|
||||
private final TelephonyManager mPhone;
|
||||
private final CarrierConfigTracker mCarrierConfigTracker;
|
||||
private final SubscriptionDefaults mDefaults;
|
||||
private final MobileMappingsProxy mMobileMappingsProxy;
|
||||
private final String mNetworkNameDefault;
|
||||
private final String mNetworkNameSeparator;
|
||||
private final ContentObserver mObserver;
|
||||
@@ -121,6 +121,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
|
||||
TelephonyManager phone,
|
||||
CallbackHandler callbackHandler,
|
||||
NetworkControllerImpl networkController,
|
||||
MobileMappingsProxy mobileMappingsProxy,
|
||||
SubscriptionInfo info,
|
||||
SubscriptionDefaults defaults,
|
||||
Looper receiverLooper,
|
||||
@@ -135,13 +136,14 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
|
||||
mPhone = phone;
|
||||
mDefaults = defaults;
|
||||
mSubscriptionInfo = info;
|
||||
mMobileMappingsProxy = mobileMappingsProxy;
|
||||
mNetworkNameSeparator = getTextIfExists(
|
||||
R.string.status_bar_network_name_separator).toString();
|
||||
mNetworkNameDefault = getTextIfExists(
|
||||
com.android.internal.R.string.lockscreen_carrier_default).toString();
|
||||
|
||||
mNetworkToIconLookup = mapIconSets(mConfig);
|
||||
mDefaultIcons = getDefaultIcons(mConfig);
|
||||
mNetworkToIconLookup = mMobileMappingsProxy.mapIconSets(mConfig);
|
||||
mDefaultIcons = mMobileMappingsProxy.getDefaultIcons(mConfig);
|
||||
|
||||
String networkName = info.getCarrierName() != null ? info.getCarrierName().toString()
|
||||
: mNetworkNameDefault;
|
||||
@@ -161,8 +163,8 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
|
||||
void setConfiguration(Config config) {
|
||||
mConfig = config;
|
||||
updateInflateSignalStrength();
|
||||
mNetworkToIconLookup = mapIconSets(mConfig);
|
||||
mDefaultIcons = getDefaultIcons(mConfig);
|
||||
mNetworkToIconLookup = mMobileMappingsProxy.mapIconSets(mConfig);
|
||||
mDefaultIcons = mMobileMappingsProxy.getDefaultIcons(mConfig);
|
||||
updateTelephony();
|
||||
}
|
||||
|
||||
@@ -271,8 +273,9 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
|
||||
dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
|
||||
}
|
||||
|
||||
final QsInfo qsInfo = getQsInfo(contentDescription, icons.dataType);
|
||||
final SbInfo sbInfo = getSbInfo(contentDescription, icons.dataType);
|
||||
int iconId = mCurrentState.getNetworkTypeIcon(mContext);
|
||||
final QsInfo qsInfo = getQsInfo(contentDescription, iconId);
|
||||
final SbInfo sbInfo = getSbInfo(contentDescription, iconId);
|
||||
|
||||
MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
|
||||
sbInfo.icon,
|
||||
@@ -373,6 +376,10 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
|
||||
} else if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
|
||||
updateDataSim();
|
||||
notifyListenersIfNecessary();
|
||||
} else if (action.equals(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED)) {
|
||||
int carrierId = intent.getIntExtra(
|
||||
TelephonyManager.EXTRA_CARRIER_ID, UNKNOWN_CARRIER_ID);
|
||||
mCurrentState.setCarrierId(carrierId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,7 +484,8 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
|
||||
mCurrentState.level = getSignalLevel(mCurrentState.signalStrength);
|
||||
}
|
||||
|
||||
String iconKey = getIconKey(mCurrentState.telephonyDisplayInfo);
|
||||
mCurrentState.setCarrierId(mPhone.getSimCarrierId());
|
||||
String iconKey = mMobileMappingsProxy.getIconKey(mCurrentState.telephonyDisplayInfo);
|
||||
if (mNetworkToIconLookup.get(iconKey) != null) {
|
||||
mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey);
|
||||
} else {
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.telephony.TelephonyManager
|
||||
import com.android.settingslib.mobile.MobileMappings
|
||||
import com.android.settingslib.mobile.MobileStatusTracker
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
|
||||
import com.android.systemui.util.CarrierConfigTracker
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -33,6 +34,7 @@ internal class MobileSignalControllerFactory @Inject constructor(
|
||||
val context: Context,
|
||||
val callbackHandler: CallbackHandler,
|
||||
val carrierConfigTracker: CarrierConfigTracker,
|
||||
val mobileMappings: MobileMappingsProxy,
|
||||
) {
|
||||
fun createMobileSignalController(
|
||||
config: MobileMappings.Config,
|
||||
@@ -56,6 +58,7 @@ internal class MobileSignalControllerFactory @Inject constructor(
|
||||
phone,
|
||||
callbackHandler,
|
||||
networkController,
|
||||
mobileMappings,
|
||||
subscriptionInfo,
|
||||
subscriptionDefaults,
|
||||
receiverLooper,
|
||||
|
||||
@@ -16,10 +16,14 @@
|
||||
|
||||
package com.android.systemui.statusbar.connectivity
|
||||
|
||||
import android.annotation.DrawableRes
|
||||
import android.content.Context
|
||||
import android.telephony.ServiceState
|
||||
import android.telephony.SignalStrength
|
||||
import android.telephony.TelephonyDisplayInfo
|
||||
import android.telephony.TelephonyManager
|
||||
import com.android.internal.annotations.VisibleForTesting
|
||||
import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||
import com.android.settingslib.Utils
|
||||
import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus
|
||||
import com.android.settingslib.mobile.TelephonyIcons
|
||||
@@ -41,7 +45,7 @@ internal class MobileState(
|
||||
@JvmField var roaming: Boolean = false,
|
||||
@JvmField var dataState: Int = TelephonyManager.DATA_DISCONNECTED,
|
||||
// Tracks the on/off state of the defaultDataSubscription
|
||||
@JvmField var defaultDataOff: Boolean = false
|
||||
@JvmField var defaultDataOff: Boolean = false,
|
||||
) : ConnectivityState() {
|
||||
|
||||
@JvmField var telephonyDisplayInfo = TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
|
||||
@@ -49,6 +53,11 @@ internal class MobileState(
|
||||
@JvmField var serviceState: ServiceState? = null
|
||||
@JvmField var signalStrength: SignalStrength? = null
|
||||
|
||||
var carrierId = TelephonyManager.UNKNOWN_CARRIER_ID
|
||||
|
||||
@VisibleForTesting
|
||||
var networkTypeResIdCache: NetworkTypeResIdCache = NetworkTypeResIdCache()
|
||||
|
||||
/** @return true if this state is disabled or not default data */
|
||||
val isDataDisabledOrNotDefault: Boolean
|
||||
get() = (iconGroup === TelephonyIcons.DATA_DISABLED ||
|
||||
@@ -125,6 +134,21 @@ internal class MobileState(
|
||||
return serviceState != null && serviceState!!.roaming
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Load the (potentially customized) icon resource id for the current network type. Note that
|
||||
* this operation caches the result. Note that reading the [MobileIconGroup.dataType] field
|
||||
* directly will not yield correct results in cases where the carrierId has an associated
|
||||
* override. This is the preferred method for getting the network type indicator.
|
||||
*
|
||||
* @return a drawable res id appropriate for the current (carrierId, networkType) pair
|
||||
*/
|
||||
@DrawableRes
|
||||
fun getNetworkTypeIcon(context: Context): Int {
|
||||
val icon = (iconGroup as MobileIconGroup)
|
||||
return networkTypeResIdCache.get(icon, carrierId, context)
|
||||
}
|
||||
|
||||
fun setFromMobileStatus(mobileStatus: MobileStatus) {
|
||||
activityIn = mobileStatus.activityIn
|
||||
activityOut = mobileStatus.activityOut
|
||||
@@ -140,6 +164,7 @@ internal class MobileState(
|
||||
super.toString(builder)
|
||||
builder.append(',')
|
||||
builder.append("dataSim=$dataSim,")
|
||||
builder.append("carrierId=$carrierId")
|
||||
builder.append("networkName=$networkName,")
|
||||
builder.append("networkNameData=$networkNameData,")
|
||||
builder.append("dataConnected=$dataConnected,")
|
||||
@@ -157,6 +182,8 @@ internal class MobileState(
|
||||
builder.append("voiceServiceState=${getVoiceServiceState()},")
|
||||
builder.append("isInService=${isInService()},")
|
||||
|
||||
builder.append("networkTypeIconCache=$networkTypeResIdCache")
|
||||
|
||||
builder.append("serviceState=${serviceState?.minLog() ?: "(null)"},")
|
||||
builder.append("signalStrength=${signalStrength?.minLog() ?: "(null)"},")
|
||||
builder.append("displayInfo=$telephonyDisplayInfo")
|
||||
@@ -164,6 +191,7 @@ internal class MobileState(
|
||||
|
||||
override fun tableColumns(): List<String> {
|
||||
val columns = listOf("dataSim",
|
||||
"carrierId",
|
||||
"networkName",
|
||||
"networkNameData",
|
||||
"dataConnected",
|
||||
@@ -178,6 +206,7 @@ internal class MobileState(
|
||||
"showQuickSettingsRatIcon",
|
||||
"voiceServiceState",
|
||||
"isInService",
|
||||
"networkTypeIconCache",
|
||||
"serviceState",
|
||||
"signalStrength",
|
||||
"displayInfo")
|
||||
@@ -187,6 +216,7 @@ internal class MobileState(
|
||||
|
||||
override fun tableData(): List<String> {
|
||||
val columns = listOf(dataSim,
|
||||
carrierId,
|
||||
networkName,
|
||||
networkNameData,
|
||||
dataConnected,
|
||||
@@ -201,6 +231,7 @@ internal class MobileState(
|
||||
showQuickSettingsRatIcon(),
|
||||
getVoiceServiceState(),
|
||||
isInService(),
|
||||
networkTypeResIdCache,
|
||||
serviceState?.minLog() ?: "(null)",
|
||||
signalStrength?.minLog() ?: "(null)",
|
||||
telephonyDisplayInfo).map { it.toString() }
|
||||
@@ -217,6 +248,7 @@ internal class MobileState(
|
||||
|
||||
if (networkName != other.networkName) return false
|
||||
if (networkNameData != other.networkNameData) return false
|
||||
if (carrierId != other.carrierId) return false
|
||||
if (dataSim != other.dataSim) return false
|
||||
if (dataConnected != other.dataConnected) return false
|
||||
if (isEmergency != other.isEmergency) return false
|
||||
@@ -238,6 +270,7 @@ internal class MobileState(
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + (networkName?.hashCode() ?: 0)
|
||||
result = 31 * result + (networkNameData?.hashCode() ?: 0)
|
||||
result = 31 * result + (carrierId.hashCode())
|
||||
result = 31 * result + dataSim.hashCode()
|
||||
result = 31 * result + dataConnected.hashCode()
|
||||
result = 31 * result + isEmergency.hashCode()
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN
|
||||
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT;
|
||||
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
|
||||
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
|
||||
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.BroadcastReceiver;
|
||||
@@ -139,7 +140,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
|
||||
private final MobileSignalControllerFactory mMobileFactory;
|
||||
|
||||
private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
|
||||
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
private int mActiveMobileDataSubscription = INVALID_SUBSCRIPTION_ID;
|
||||
|
||||
// Subcontrollers.
|
||||
@VisibleForTesting
|
||||
@@ -503,6 +504,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
|
||||
filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
|
||||
filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
|
||||
filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
|
||||
filter.addAction(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
|
||||
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
|
||||
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
|
||||
mListening = true;
|
||||
@@ -793,6 +795,20 @@ public class NetworkControllerImpl extends BroadcastReceiver
|
||||
mConfig = Config.readConfig(mContext);
|
||||
mReceiverHandler.post(this::handleConfigurationChanged);
|
||||
break;
|
||||
|
||||
case TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED: {
|
||||
// Notify the relevant MobileSignalController of the change
|
||||
int subId = intent.getIntExtra(
|
||||
TelephonyManager.EXTRA_SUBSCRIPTION_ID,
|
||||
INVALID_SUBSCRIPTION_ID
|
||||
);
|
||||
if (SubscriptionManager.isValidSubscriptionId(subId)) {
|
||||
if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
|
||||
mMobileSignalControllers.get(subId).handleBroadcast(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Intent.ACTION_SIM_STATE_CHANGED:
|
||||
// Avoid rebroadcast because SysUI is direct boot aware.
|
||||
if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
|
||||
@@ -820,7 +836,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
|
||||
break;
|
||||
default:
|
||||
int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
|
||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||
INVALID_SUBSCRIPTION_ID);
|
||||
if (SubscriptionManager.isValidSubscriptionId(subId)) {
|
||||
if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
|
||||
mMobileSignalControllers.get(subId).handleBroadcast(intent);
|
||||
@@ -1336,6 +1352,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
|
||||
String slotString = args.getString("slot");
|
||||
int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString);
|
||||
slot = MathUtils.constrain(slot, 0, 8);
|
||||
String carrierIdString = args.getString("carrierid");
|
||||
int carrierId = TextUtils.isEmpty(carrierIdString) ? 0
|
||||
: Integer.parseInt(carrierIdString);
|
||||
// Ensure we have enough sim slots
|
||||
List<SubscriptionInfo> subs = new ArrayList<>();
|
||||
while (mMobileSignalControllers.size() <= slot) {
|
||||
@@ -1347,6 +1366,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
|
||||
}
|
||||
// Hack to index linearly for easy use.
|
||||
MobileSignalController controller = mMobileSignalControllers.valueAt(slot);
|
||||
if (carrierId != 0) {
|
||||
controller.getState().setCarrierId(carrierId);
|
||||
}
|
||||
controller.getState().dataSim = datatype != null;
|
||||
controller.getState().isDefault = datatype != null;
|
||||
controller.getState().dataConnected = datatype != null;
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.systemui.statusbar.connectivity
|
||||
|
||||
import android.annotation.DrawableRes
|
||||
import android.content.Context
|
||||
import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||
import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
|
||||
import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl
|
||||
|
||||
/**
|
||||
* Cache for network type resource IDs.
|
||||
*
|
||||
* The default framework behavior is to have a statically defined icon per network type. See
|
||||
* [MobileIconGroup] for the standard mapping.
|
||||
*
|
||||
* For the case of carrierId-defined overrides, we want to check [MobileIconCarrierIdOverrides] for
|
||||
* an existing icon override, and cache the result of the operation
|
||||
*/
|
||||
class NetworkTypeResIdCache(
|
||||
private val overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
|
||||
) {
|
||||
@DrawableRes private var cachedResId: Int = 0
|
||||
private var lastCarrierId: Int? = null
|
||||
private var lastIconGroup: MobileIconGroup? = null
|
||||
private var isOverridden: Boolean = false
|
||||
|
||||
@DrawableRes
|
||||
fun get(iconGroup: MobileIconGroup, carrierId: Int, context: Context): Int {
|
||||
if (lastCarrierId != carrierId || lastIconGroup != iconGroup) {
|
||||
lastCarrierId = carrierId
|
||||
lastIconGroup = iconGroup
|
||||
|
||||
val maybeOverride = calculateOverriddenIcon(iconGroup, carrierId, context)
|
||||
if (maybeOverride > 0) {
|
||||
cachedResId = maybeOverride
|
||||
isOverridden = true
|
||||
} else {
|
||||
cachedResId = iconGroup.dataType
|
||||
isOverridden = false
|
||||
}
|
||||
}
|
||||
|
||||
return cachedResId
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "networkTypeResIdCache={id=$cachedResId, isOverridden=$isOverridden}"
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
private fun calculateOverriddenIcon(
|
||||
iconGroup: MobileIconGroup,
|
||||
carrierId: Int,
|
||||
context: Context,
|
||||
): Int {
|
||||
val name = iconGroup.name
|
||||
if (!overrides.carrierIdEntryExists(carrierId)) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return overrides.getOverrideFor(carrierId, name, context.resources)
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,13 @@ import javax.inject.Inject
|
||||
* own [Configuration] and track resources based on the full set of available mcc-mnc combinations.
|
||||
*
|
||||
* (for future reference: b/240555502 is the initiating bug for this)
|
||||
*
|
||||
* NOTE: MCC/MNC qualifiers are not sufficient to fully describe a network type icon qualified by
|
||||
* network type + carrier ID. This class exists to keep the legacy behavior of using the MCC/MNC
|
||||
* resource qualifiers working, but if a carrier-specific icon is requested, then the override
|
||||
* provided by [MobileIconCarrierIdOverrides] will take precedence.
|
||||
*
|
||||
* TODO(b/258503704): consider removing this class in favor of the `carrierId` overrides
|
||||
*/
|
||||
@SysUISingleton
|
||||
class MobileContextProvider
|
||||
|
||||
@@ -32,6 +32,7 @@ import javax.inject.Inject
|
||||
interface MobileMappingsProxy {
|
||||
fun mapIconSets(config: Config): Map<String, MobileIconGroup>
|
||||
fun getDefaultIcons(config: Config): MobileIconGroup
|
||||
fun getIconKey(displayInfo: TelephonyDisplayInfo): String
|
||||
fun toIconKey(@NetworkType networkType: Int): String
|
||||
fun toIconKeyOverride(@NetworkType networkType: Int): String
|
||||
}
|
||||
@@ -44,6 +45,9 @@ class MobileMappingsProxyImpl @Inject constructor() : MobileMappingsProxy {
|
||||
override fun getDefaultIcons(config: Config): MobileIconGroup =
|
||||
MobileMappings.getDefaultIcons(config)
|
||||
|
||||
override fun getIconKey(displayInfo: TelephonyDisplayInfo): String =
|
||||
MobileMappings.getIconKey(displayInfo)
|
||||
|
||||
override fun toIconKey(@NetworkType networkType: Int): String =
|
||||
MobileMappings.toIconKey(networkType)
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.systemui.statusbar.connectivity
|
||||
|
||||
import android.content.res.Resources
|
||||
import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
|
||||
|
||||
typealias CarrierId = Int
|
||||
|
||||
typealias NetworkType = String
|
||||
|
||||
typealias ResId = Int
|
||||
|
||||
class MobileIconCarrierIdOverridesFake : MobileIconCarrierIdOverrides {
|
||||
/** Backing for [carrierIdEntryExists] */
|
||||
var overriddenIds = mutableSetOf<Int>()
|
||||
|
||||
/** Backing for [getOverrideFor]. Map should be Map< CarrierId < NetworkType, ResId>> */
|
||||
var overridesByCarrierId = mutableMapOf<CarrierId, Map<NetworkType, ResId>>()
|
||||
|
||||
override fun getOverrideFor(
|
||||
carrierId: CarrierId,
|
||||
networkType: NetworkType,
|
||||
resources: Resources
|
||||
): ResId {
|
||||
if (!overriddenIds.contains(carrierId)) return 0
|
||||
|
||||
return overridesByCarrierId[carrierId]?.get(networkType) ?: 0
|
||||
}
|
||||
|
||||
override fun carrierIdEntryExists(carrierId: Int): Boolean {
|
||||
return overriddenIds.contains(carrierId)
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.systemui.statusbar.connectivity;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
|
||||
import com.android.settingslib.mobile.TelephonyIcons;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
public class MobileStateTest extends SysuiTestCase {
|
||||
|
||||
private final MobileState mState = new MobileState();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsDataDisabledOrNotDefault_dataDisabled() {
|
||||
mState.iconGroup = TelephonyIcons.DATA_DISABLED;
|
||||
mState.userSetup = true;
|
||||
|
||||
assertTrue(mState.isDataDisabledOrNotDefault());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsDataDisabledOrNotDefault_notDefaultData() {
|
||||
mState.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA;
|
||||
mState.userSetup = true;
|
||||
|
||||
assertTrue(mState.isDataDisabledOrNotDefault());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsDataDisabledOrNotDefault_notDisabled() {
|
||||
mState.iconGroup = TelephonyIcons.G;
|
||||
mState.userSetup = true;
|
||||
|
||||
assertFalse(mState.isDataDisabledOrNotDefault());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasActivityIn_noData_noActivity() {
|
||||
mState.dataConnected = false;
|
||||
mState.carrierNetworkChangeMode = false;
|
||||
mState.activityIn = false;
|
||||
|
||||
assertFalse(mState.hasActivityIn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasActivityIn_noData_activityIn() {
|
||||
mState.dataConnected = false;
|
||||
mState.carrierNetworkChangeMode = false;
|
||||
mState.activityIn = true;
|
||||
|
||||
assertFalse(mState.hasActivityIn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasActivityIn_dataConnected_activityIn() {
|
||||
mState.dataConnected = true;
|
||||
mState.carrierNetworkChangeMode = false;
|
||||
mState.activityIn = true;
|
||||
|
||||
assertTrue(mState.hasActivityIn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasActivityIn_carrierNetworkChange() {
|
||||
mState.dataConnected = true;
|
||||
mState.carrierNetworkChangeMode = true;
|
||||
mState.activityIn = true;
|
||||
|
||||
assertFalse(mState.hasActivityIn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasActivityOut_noData_noActivity() {
|
||||
mState.dataConnected = false;
|
||||
mState.carrierNetworkChangeMode = false;
|
||||
mState.activityOut = false;
|
||||
|
||||
assertFalse(mState.hasActivityOut());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasActivityOut_noData_activityOut() {
|
||||
mState.dataConnected = false;
|
||||
mState.carrierNetworkChangeMode = false;
|
||||
mState.activityOut = true;
|
||||
|
||||
assertFalse(mState.hasActivityOut());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasActivityOut_dataConnected_activityOut() {
|
||||
mState.dataConnected = true;
|
||||
mState.carrierNetworkChangeMode = false;
|
||||
mState.activityOut = true;
|
||||
|
||||
assertTrue(mState.hasActivityOut());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasActivityOut_carrierNetworkChange() {
|
||||
mState.dataConnected = true;
|
||||
mState.carrierNetworkChangeMode = true;
|
||||
mState.activityOut = true;
|
||||
|
||||
assertFalse(mState.hasActivityOut());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.systemui.statusbar.connectivity
|
||||
|
||||
import android.test.suitebuilder.annotation.SmallTest
|
||||
import android.testing.AndroidTestingRunner
|
||||
import com.android.settingslib.mobile.TelephonyIcons
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import junit.framework.Assert.assertFalse
|
||||
import junit.framework.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class MobileStateTest : SysuiTestCase() {
|
||||
|
||||
private val state = MobileState()
|
||||
@Before fun setUp() {}
|
||||
|
||||
@Test
|
||||
fun testIsDataDisabledOrNotDefault_dataDisabled() {
|
||||
state.iconGroup = TelephonyIcons.DATA_DISABLED
|
||||
state.userSetup = true
|
||||
assertTrue(state.isDataDisabledOrNotDefault)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsDataDisabledOrNotDefault_notDefaultData() {
|
||||
state.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA
|
||||
state.userSetup = true
|
||||
assertTrue(state.isDataDisabledOrNotDefault)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsDataDisabledOrNotDefault_notDisabled() {
|
||||
state.iconGroup = TelephonyIcons.G
|
||||
state.userSetup = true
|
||||
assertFalse(state.isDataDisabledOrNotDefault)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHasActivityIn_noData_noActivity() {
|
||||
state.dataConnected = false
|
||||
state.carrierNetworkChangeMode = false
|
||||
state.activityIn = false
|
||||
assertFalse(state.hasActivityIn())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHasActivityIn_noData_activityIn() {
|
||||
state.dataConnected = false
|
||||
state.carrierNetworkChangeMode = false
|
||||
state.activityIn = true
|
||||
assertFalse(state.hasActivityIn())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHasActivityIn_dataConnected_activityIn() {
|
||||
state.dataConnected = true
|
||||
state.carrierNetworkChangeMode = false
|
||||
state.activityIn = true
|
||||
assertTrue(state.hasActivityIn())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHasActivityIn_carrierNetworkChange() {
|
||||
state.dataConnected = true
|
||||
state.carrierNetworkChangeMode = true
|
||||
state.activityIn = true
|
||||
assertFalse(state.hasActivityIn())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHasActivityOut_noData_noActivity() {
|
||||
state.dataConnected = false
|
||||
state.carrierNetworkChangeMode = false
|
||||
state.activityOut = false
|
||||
assertFalse(state.hasActivityOut())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHasActivityOut_noData_activityOut() {
|
||||
state.dataConnected = false
|
||||
state.carrierNetworkChangeMode = false
|
||||
state.activityOut = true
|
||||
assertFalse(state.hasActivityOut())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHasActivityOut_dataConnected_activityOut() {
|
||||
state.dataConnected = true
|
||||
state.carrierNetworkChangeMode = false
|
||||
state.activityOut = true
|
||||
assertTrue(state.hasActivityOut())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHasActivityOut_carrierNetworkChange() {
|
||||
state.dataConnected = true
|
||||
state.carrierNetworkChangeMode = true
|
||||
state.activityOut = true
|
||||
assertFalse(state.hasActivityOut())
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.demomode.DemoModeController;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.plugins.log.LogBuffer;
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy;
|
||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
|
||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
|
||||
import com.android.systemui.telephony.TelephonyListenerManager;
|
||||
@@ -125,6 +126,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
|
||||
protected CarrierConfigTracker mCarrierConfigTracker;
|
||||
protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
protected Handler mMainHandler;
|
||||
// Use a real mobile mappings object since lots of tests rely on it
|
||||
protected FakeMobileMappingsProxy mMobileMappingsProxy = new FakeMobileMappingsProxy();
|
||||
protected WifiStatusTrackerFactory mWifiStatusTrackerFactory;
|
||||
protected MobileSignalControllerFactory mMobileFactory;
|
||||
|
||||
@@ -219,10 +222,13 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
|
||||
|
||||
mWifiStatusTrackerFactory = new WifiStatusTrackerFactory(
|
||||
mContext, mMockWm, mMockNsm, mMockCm, mMainHandler);
|
||||
// Most of these tests rely on the actual MobileMappings behavior
|
||||
mMobileMappingsProxy.setUseRealImpl(true);
|
||||
mMobileFactory = new MobileSignalControllerFactory(
|
||||
mContext,
|
||||
mCallbackHandler,
|
||||
mCarrierConfigTracker
|
||||
mCarrierConfigTracker,
|
||||
mMobileMappingsProxy
|
||||
);
|
||||
|
||||
mNetworkController = new NetworkControllerImpl(mContext,
|
||||
|
||||
@@ -18,12 +18,21 @@ package com.android.systemui.statusbar.connectivity;
|
||||
|
||||
import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
|
||||
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
|
||||
import static android.telephony.TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED;
|
||||
import static android.telephony.TelephonyManager.EXTRA_CARRIER_ID;
|
||||
import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID;
|
||||
|
||||
import static com.android.settingslib.mobile.TelephonyIcons.NR_5G_PLUS;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@@ -35,6 +44,7 @@ import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
|
||||
import com.android.settingslib.SignalIcon.MobileIconGroup;
|
||||
import com.android.settingslib.mobile.TelephonyIcons;
|
||||
import com.android.settingslib.net.DataUsageController;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
@@ -45,6 +55,8 @@ import com.android.systemui.util.CarrierConfigTracker;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper
|
||||
@@ -329,6 +341,57 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
|
||||
assertFalse(mNetworkController.isMobileDataNetworkInService());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mobileSignalController_getsCarrierId() {
|
||||
when(mMockTm.getSimCarrierId()).thenReturn(1);
|
||||
setupDefaultSignal();
|
||||
|
||||
assertEquals(1, mMobileSignalController.getState().getCarrierId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mobileSignalController_updatesCarrierId_onChange() {
|
||||
when(mMockTm.getSimCarrierId()).thenReturn(1);
|
||||
setupDefaultSignal();
|
||||
|
||||
// Updates are sent down through this broadcast, we can send the intent directly
|
||||
Intent intent = new Intent(ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
|
||||
intent.putExtra(EXTRA_SUBSCRIPTION_ID, mSubId);
|
||||
intent.putExtra(EXTRA_CARRIER_ID, 2);
|
||||
|
||||
mMobileSignalController.handleBroadcast(intent);
|
||||
|
||||
assertEquals(2, mMobileSignalController.getState().getCarrierId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void networkTypeIcon_hasCarrierIdOverride() {
|
||||
int fakeCarrier = 1;
|
||||
int fakeIconOverride = 12345;
|
||||
int testDataNetType = 100;
|
||||
String testDataString = "100";
|
||||
HashMap<String, MobileIconGroup> testMap = new HashMap<>();
|
||||
testMap.put(testDataString, NR_5G_PLUS);
|
||||
|
||||
// Pretend that there is an override for this icon, and this carrier ID
|
||||
NetworkTypeResIdCache mockCache = mock(NetworkTypeResIdCache.class);
|
||||
when(mockCache.get(eq(NR_5G_PLUS), eq(fakeCarrier), any())).thenReturn(fakeIconOverride);
|
||||
|
||||
// Turn off the default mobile mapping, so we can override
|
||||
mMobileMappingsProxy.setUseRealImpl(false);
|
||||
mMobileMappingsProxy.setIconMap(testMap);
|
||||
// Use the mocked cache
|
||||
mMobileSignalController.mCurrentState.setNetworkTypeResIdCache(mockCache);
|
||||
// Rebuild the network map
|
||||
mMobileSignalController.setConfiguration(mConfig);
|
||||
when(mMockTm.getSimCarrierId()).thenReturn(fakeCarrier);
|
||||
|
||||
setupDefaultSignal();
|
||||
updateDataConnectionState(TelephonyManager.DATA_CONNECTED, testDataNetType);
|
||||
|
||||
verifyDataIndicators(fakeIconOverride);
|
||||
}
|
||||
|
||||
private void testDataActivity(int direction, boolean in, boolean out) {
|
||||
updateDataActivity(direction);
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.systemui.statusbar.connectivity
|
||||
|
||||
import android.test.suitebuilder.annotation.SmallTest
|
||||
import android.testing.AndroidTestingRunner
|
||||
import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class NetworkTypeResIdCacheTest : SysuiTestCase() {
|
||||
private lateinit var cache: NetworkTypeResIdCache
|
||||
private var overrides = MobileIconCarrierIdOverridesFake()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
cache = NetworkTypeResIdCache(overrides)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun carrier1_noOverride_usesDefault() {
|
||||
assertThat(cache.get(group1, CARRIER_1, context)).isEqualTo(iconDefault1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun carrier1_overridden_usesOverride() {
|
||||
overrides.overriddenIds.add(CARRIER_1)
|
||||
overrides.overridesByCarrierId[CARRIER_1] = mapOf(NET_TYPE_1 to iconOverride1)
|
||||
|
||||
assertThat(cache.get(group1, CARRIER_1, context)).isEqualTo(iconOverride1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun carrier1_override_carrier2UsesDefault() {
|
||||
overrides.overriddenIds.add(CARRIER_1)
|
||||
overrides.overridesByCarrierId[CARRIER_1] = mapOf(NET_TYPE_1 to iconOverride1)
|
||||
|
||||
assertThat(cache.get(group1, CARRIER_2, context)).isEqualTo(iconDefault1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun carrier1_overrideType1_type2UsesDefault() {
|
||||
overrides.overriddenIds.add(CARRIER_1)
|
||||
overrides.overridesByCarrierId[CARRIER_1] = mapOf(NET_TYPE_1 to iconOverride1)
|
||||
|
||||
assertThat(cache.get(group2, CARRIER_1, context)).isEqualTo(iconDefault2)
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Simplified icon overrides here
|
||||
const val CARRIER_1 = 1
|
||||
const val CARRIER_2 = 2
|
||||
|
||||
const val NET_TYPE_1 = "one"
|
||||
const val iconDefault1 = 123
|
||||
const val iconOverride1 = 321
|
||||
val group1 = MobileIconGroup(NET_TYPE_1, /* dataContentDesc */ 0, iconDefault1)
|
||||
|
||||
const val NET_TYPE_2 = "two"
|
||||
const val iconDefault2 = 234
|
||||
|
||||
val group2 = MobileIconGroup(NET_TYPE_2, /* dataContentDesc*/ 0, iconDefault2)
|
||||
}
|
||||
}
|
||||
@@ -16,31 +16,59 @@
|
||||
|
||||
package com.android.systemui.statusbar.pipeline.mobile.util
|
||||
|
||||
import android.telephony.TelephonyDisplayInfo
|
||||
import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||
import com.android.settingslib.mobile.MobileMappings.Config
|
||||
import com.android.settingslib.mobile.TelephonyIcons
|
||||
|
||||
class FakeMobileMappingsProxy : MobileMappingsProxy {
|
||||
// The old [NetworkControllerDataTest] infra requires us to be able to use the real
|
||||
// impl sometimes
|
||||
var useRealImpl = false
|
||||
|
||||
private var realImpl = MobileMappingsProxyImpl()
|
||||
private var iconMap = mapOf<String, MobileIconGroup>()
|
||||
private var defaultIcons = TelephonyIcons.THREE_G
|
||||
|
||||
fun setIconMap(map: Map<String, MobileIconGroup>) {
|
||||
iconMap = map
|
||||
}
|
||||
override fun mapIconSets(config: Config): Map<String, MobileIconGroup> = iconMap
|
||||
override fun mapIconSets(config: Config): Map<String, MobileIconGroup> {
|
||||
if (useRealImpl) {
|
||||
return realImpl.mapIconSets(config)
|
||||
}
|
||||
return iconMap
|
||||
}
|
||||
fun getIconMap() = iconMap
|
||||
|
||||
fun setDefaultIcons(group: MobileIconGroup) {
|
||||
defaultIcons = group
|
||||
}
|
||||
override fun getDefaultIcons(config: Config): MobileIconGroup = defaultIcons
|
||||
override fun getDefaultIcons(config: Config): MobileIconGroup {
|
||||
if (useRealImpl) {
|
||||
return realImpl.getDefaultIcons(config)
|
||||
}
|
||||
return defaultIcons
|
||||
}
|
||||
|
||||
/** This is only used in the old pipeline, use the real impl always */
|
||||
override fun getIconKey(displayInfo: TelephonyDisplayInfo): String {
|
||||
return realImpl.getIconKey(displayInfo)
|
||||
}
|
||||
|
||||
fun getDefaultIcons(): MobileIconGroup = defaultIcons
|
||||
|
||||
override fun toIconKey(networkType: Int): String {
|
||||
if (useRealImpl) {
|
||||
return realImpl.toIconKeyOverride(networkType)
|
||||
}
|
||||
return networkType.toString()
|
||||
}
|
||||
|
||||
override fun toIconKeyOverride(networkType: Int): String {
|
||||
if (useRealImpl) {
|
||||
return realImpl.toIconKeyOverride(networkType)
|
||||
}
|
||||
return toIconKey(networkType) + "_override"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user