Introduce Telephony2gUpdater to Apply 2g User Restrictions

Listens for the following actions and ensures that 2g is enabled
or disabled based on the state of the `DISALLOW_2G` user
restriction. 2g is enabled or disabled for all active subscriptions
on the device.

* A broadcast for action `android.os.action.USER_RESTRICTIONS_CHANGED`
* A callback from `SubscriptionManager` informing us of a subscription
state change

Test: atest Telephony2gUpdaterTest
Bug: 230669497
Change-Id: I819d412bc14cb98a5874d2a42b50d4e4483b4b02
diff --git a/tests/src/com/android/TestContext.java b/tests/src/com/android/TestContext.java
index 7edbed9..7c3a842 100644
--- a/tests/src/com/android/TestContext.java
+++ b/tests/src/com/android/TestContext.java
@@ -31,6 +31,7 @@
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.Process;
+import android.os.UserManager;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
@@ -58,6 +59,7 @@
     @Mock TelephonyManager mMockTelephonyManager;
     @Mock SubscriptionManager mMockSubscriptionManager;
     @Mock ImsManager mMockImsManager;
+    @Mock UserManager mMockUserManager;
 
     private SparseArray<PersistableBundle> mCarrierConfigs = new SparseArray<>();
 
@@ -147,6 +149,9 @@
             case(Context.TELEPHONY_IMS_SERVICE) : {
                 return mMockImsManager;
             }
+            case(Context.USER_SERVICE) : {
+                return mMockUserManager;
+            }
         }
         return null;
     }
@@ -165,6 +170,9 @@
         if (serviceClass == SubscriptionManager.class) {
             return Context.TELEPHONY_SUBSCRIPTION_SERVICE;
         }
+        if (serviceClass == UserManager.class) {
+            return Context.USER_SERVICE;
+        }
         return null;
     }
 
diff --git a/tests/src/com/android/phone/Telephony2gUpdaterTest.java b/tests/src/com/android/phone/Telephony2gUpdaterTest.java
new file mode 100644
index 0000000..3443767
--- /dev/null
+++ b/tests/src/com/android/phone/Telephony2gUpdaterTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.phone;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.UserManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+@RunWith(AndroidJUnit4.class)
+public class Telephony2gUpdaterTest extends TelephonyTestBase {
+    private Telephony2gUpdater mTelephony2gUpdater;
+    private Executor mExecutor;
+
+    private UserManager mMockUserManager;
+    private TelephonyManager mMockTelephonyManager;
+    private SubscriptionManager mMockSubscriptionManager;
+
+    // 2G Bitmask is 0b10000000_01001011
+    private static final long BASE_NETWORK = 0b11111111_11111111;
+    private static final long EXPECTED_DISABLED = 0b01111111_10110100;
+    private static final long EXPECTED_ENABLED = 0b11111111_11111111;
+
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mMockTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+        mMockUserManager = mContext.getSystemService(UserManager.class);
+        mMockSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
+
+        mExecutor = Executors.newSingleThreadExecutor();
+        mTelephony2gUpdater = new Telephony2gUpdater(mExecutor,
+                getTestContext(), BASE_NETWORK);
+    }
+
+    @Test
+    public void handleUserRestrictionsChanged_noSubscriptions_noAllowedNetworksChanged() {
+        when(mMockSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
+                new ArrayList<>());
+        mTelephony2gUpdater.handleUserRestrictionsChanged(getTestContext());
+        verify(mMockTelephonyManager, never()).setAllowedNetworkTypesForReason(anyInt(), anyInt());
+    }
+
+    @Test
+    public void handleUserRestrictionsChanged_nullSubscriptions_noAllowedNetworksChanged() {
+        when(mMockSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(null);
+        mTelephony2gUpdater.handleUserRestrictionsChanged(getTestContext());
+        verify(mMockTelephonyManager, never()).setAllowedNetworkTypesForReason(anyInt(), anyInt());
+    }
+
+    @Test
+    public void handleUserRestrictionsChanged_oneSubscription_allowedNetworksUpdated() {
+        TelephonyManager tmSubscription1 = mock(TelephonyManager.class);
+        when(mMockSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
+                Collections.singletonList(getSubInfo(1)));
+        when(mMockTelephonyManager.createForSubscriptionId(1)).thenReturn(tmSubscription1);
+        when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
+                true);
+
+        mTelephony2gUpdater.handleUserRestrictionsChanged(getTestContext());
+
+        System.out.println(TelephonyManager.convertNetworkTypeBitmaskToString(11L));
+        verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
+    }
+
+    @Test
+    public void handleUserRestrictionsChanged_manySubscriptionsDisallow2g_allowedNetworkUpdated() {
+
+        // Two subscriptions are available
+        when(mMockSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
+                Arrays.asList(getSubInfo(1), getSubInfo(2)));
+        TelephonyManager tmSubscription1 = mock(TelephonyManager.class);
+        TelephonyManager tmSubscription2 = mock(TelephonyManager.class);
+        when(mMockTelephonyManager.createForSubscriptionId(1)).thenReturn(tmSubscription1);
+        when(mMockTelephonyManager.createForSubscriptionId(2)).thenReturn(tmSubscription2);
+        // 2g is disallowed
+        when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
+                true);
+
+        mTelephony2gUpdater.handleUserRestrictionsChanged(getTestContext());
+
+        verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
+        verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
+    }
+
+    @Test
+    public void handleUserRestrictionsChanged_manySubscriptionsAllow2g_allowedNetworkUpdated() {
+
+        // Two subscriptions are available
+        when(mMockSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
+                Arrays.asList(getSubInfo(1), getSubInfo(2)));
+        TelephonyManager tmSubscription1 = mock(TelephonyManager.class);
+        TelephonyManager tmSubscription2 = mock(TelephonyManager.class);
+        when(mMockTelephonyManager.createForSubscriptionId(1)).thenReturn(tmSubscription1);
+        when(mMockTelephonyManager.createForSubscriptionId(2)).thenReturn(tmSubscription2);
+
+        // 2g is allowed
+        when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
+                false);
+
+        mTelephony2gUpdater.handleUserRestrictionsChanged(getTestContext());
+
+        verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+        verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+    }
+
+    private SubscriptionInfo getSubInfo(int id) {
+        return new SubscriptionInfo(id, "890126042XXXXXXXXXXX", 0, "T-mobile", "T-mobile", 0, 255,
+                "12345", 0, null, "310", "260", "156", false, null, null);
+    }
+}