Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 53fc0cc0 authored by Gil Cukierman's avatar Gil Cukierman
Browse files

Handle Device Admin Updates to 2G per Phone Instance

Problem:
A previous implementation (aosp/2371949) honoring the no_cellular_2g restriction
operated at the subscription level and was complex and prone to race
conditions between subscription loading and carrier configs. This
manifested in devices booting without applying the admin policy
sometimes.

Solution:
Move the enforcement of the admin policy into each instance
of Phone. Before allowed network types are sent to the modem, a
check against the admin policy is performed. If the no_cellular_2g
restriction is set, 2g is disabled in the effective bitmap that is
passed to the modem. Note that Phone.sendSubscriptionSetting is called
by ServiceStateTracker when a new subscription is added so this change
is piggy backing off of that call to ensure the admin policy gets
enforced on every call.

Introduce BroadcastReceiver, TelephonyAdminReceiver, that calls
Phone.sendSubscriptionSettings when the relevant 2g admin policy
changes.

Bug: 266472206
Test: atest TelephonyAdminReceiver
Test: atest GsmCdmaPhoneTest
Test: Manual testing on cuttlefish with the TestDPC app. Asserted that
the 2g admin policy is applied on boot and persisted across reboots.

Change-Id: Iaf15dca36c4a1b383d75b65d157578fdeb4ad989
parent 58ddf02f
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -280,6 +280,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {

    public static final String PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED =
            "pref_null_cipher_and_integrity_enabled";
    private final TelephonyAdminReceiver m2gAdminUpdater;

    /**
     * This method is invoked when the Phone exits Emergency Callback Mode.
@@ -636,6 +637,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
        if (isSubscriptionManagerServiceEnabled()) {
            mSubscriptionManagerService = SubscriptionManagerService.getInstance();
        }
        m2gAdminUpdater = new TelephonyAdminReceiver(context, this);

        if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
            return;
@@ -2324,6 +2326,12 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
        if (!mIsCarrierNrSupported) {
            allowedNetworkTypes &= ~TelephonyManager.NETWORK_TYPE_BITMASK_NR;
        }
        if (m2gAdminUpdater.isCellular2gDisabled()) {
            logd("SubId " + getSubId()
                    + " disabling 2g in getEffectiveAllowedNetworkTypes according to admin user "
                    + "restriction");
            allowedNetworkTypes &= ~TelephonyManager.NETWORK_CLASS_BITMASK_2G;
        }
        logd("SubId" + getSubId() + ",getEffectiveAllowedNetworkTypes: "
                + TelephonyManager.convertNetworkTypeBitmaskToString(allowedNetworkTypes));
        return allowedNetworkTypes;
+100 −0
Original line number Diff line number Diff line
/*
 * 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.internal.telephony;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.UserManager;
import android.telephony.Rlog;

/**
 * A BroadcastReceiver for device administration events.
 */
public class TelephonyAdminReceiver extends BroadcastReceiver {
    private static final String TAG = "TelephonyAdminReceiver";
    private final Phone mPhone;
    // We keep track of the last value to avoid updating when unrelated user restrictions change
    private boolean mDisallowCellular2gRestriction = false;
    private final Context mContext;
    private UserManager mUserManager;

    public TelephonyAdminReceiver(Context context, Phone phone) {
        mContext = context;
        mPhone = phone;
        mUserManager = null;
        if (ensureUserManagerExists()) {
            mDisallowCellular2gRestriction = mUserManager.hasUserRestriction(
                    UserManager.DISALLOW_CELLULAR_2G);
        }
        IntentFilter filter = new IntentFilter();
        filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
        context.registerReceiver(this, filter);
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Rlog.d(TAG, "Processing onReceive");
        if (context == null || intent == null) return;
        if (!intent.getAction().equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) {
            Rlog.d(TAG, "Ignoring unexpected action: " + intent.getAction());
            return;
        }
        if (!ensureUserManagerExists()) {
            return;
        }
        boolean shouldDisallow2g = mUserManager.hasUserRestriction(
                UserManager.DISALLOW_CELLULAR_2G);

        if (shouldDisallow2g != mDisallowCellular2gRestriction) {
            Rlog.i(TAG,
                    "Updating allowed network types with new admin 2g restriction. no_cellular_2g: "
                            + shouldDisallow2g);
            mDisallowCellular2gRestriction = shouldDisallow2g;
            mPhone.sendSubscriptionSettings(false);
        } else {
            Rlog.i(TAG, "Skipping update of allowed network types. Restriction no_cellular_2g "
                    + "unchanged: " + mDisallowCellular2gRestriction);
        }
    }

    /**
     * Returns the current state of the {@link UserManager#DISALLOW_CELLULAR_2G} user restriction.
     */
    public boolean isCellular2gDisabled() {
        return mDisallowCellular2gRestriction;
    }

    /**
     * Tries to resolve the user manager system service. Returns true if successful, false
     * otherwise.
     */
    private boolean ensureUserManagerExists() {
        if (mUserManager == null) {
            Rlog.d(TAG, "No user manager. Attempting to resolve one.");
            mUserManager = mContext.getSystemService(UserManager.class);
        }
        if (mUserManager == null) {
            Rlog.e(TAG,
                    "Could not get a user manager instance. All operations will be no-ops until "
                            + "one is resolved");
            return false;
        }
        return true;
    }
}
+27 −0
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserManager;
import android.os.WorkSource;
import android.preference.PreferenceManager;
import android.provider.DeviceConfig;
@@ -92,6 +93,7 @@ import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.domainselection.DomainSelectionResolver;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.test.SimulatedCommands;
import com.android.internal.telephony.test.SimulatedCommandsVerifier;
import com.android.internal.telephony.uicc.AdnRecord;
@@ -2685,4 +2687,29 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
        mPhoneUT.handleMessage(message);
        verify(mSimulatedCommandsVerifier, times(2)).getImei(nullable(Message.class));
    }

    @Test
    public void testSetAllowedNetworkTypes_admin2gRestrictionHonored() throws Exception {
        // circumvent loading/saving to sim db. it's not behavior under test.
        TelephonyManager.setupISubForTest(Mockito.mock(SubscriptionManagerService.class));
        TelephonyManager.enableServiceHandleCaching();
        mPhoneUT.loadAllowedNetworksFromSubscriptionDatabase();

        // 2g is disabled by admin
        UserManager userManagerMock = mContext.getSystemService(UserManager.class);
        when(userManagerMock.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(true);
        mContext.sendBroadcast(new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));

        // carrier requests 2g to be enabled
        mPhoneUT.setAllowedNetworkTypes(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER,
                TelephonyManager.NETWORK_CLASS_BITMASK_2G, null);

        // Assert that 2g was not passed as an allowed network type to the modem
        ArgumentCaptor<Integer> captureBitMask = ArgumentCaptor.forClass(Integer.class);
        // One call for the admin restriction update, another by this test
        verify(mSimulatedCommandsVerifier, times(2)).setAllowedNetworkTypesBitmap(
                captureBitMask.capture(),
                nullable(Message.class));
        assertEquals(0, captureBitMask.getValue() & TelephonyManager.NETWORK_CLASS_BITMASK_2G);
    }
}
+89 −0
Original line number Diff line number Diff line
/*
 * 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.internal.telephony;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
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.content.Intent;
import android.os.UserManager;

import androidx.test.ext.junit.runners.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class TelephonyAdminReceiverTest extends TelephonyTest {

    private TelephonyAdminReceiver mTelephonyAdminReceiver;

    @Before
    public void setUp() throws Exception {
        super.setUp(getClass().getSimpleName());
        mTelephonyAdminReceiver = new TelephonyAdminReceiver(mContext, mPhone);
    }

    @Test
    public void test_nullUserManager() {
        mUserManager = null;
        TelephonyAdminReceiver telephonyAdminReceiver = new TelephonyAdminReceiver(mContext,
                mPhone);
        assertFalse(telephonyAdminReceiver.isCellular2gDisabled());
    }

    @Test
    public void test_nullIntent_noUpdate() {
        assertFalse(mTelephonyAdminReceiver.isCellular2gDisabled());

        mContext.sendBroadcast(new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));

        verify(mPhone, never()).sendSubscriptionSettings(anyBoolean());
        assertFalse(mTelephonyAdminReceiver.isCellular2gDisabled());
    }

    @Test
    public void test_userRestrictionsNotChanged_noUpdate() {
        assertFalse(mTelephonyAdminReceiver.isCellular2gDisabled());
        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(false);

        mContext.sendBroadcast(new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));

        verify(mPhone, never()).sendSubscriptionSettings(anyBoolean());
        assertFalse(mTelephonyAdminReceiver.isCellular2gDisabled());
    }

    @Test
    public void test_userRestrictionToggled_shouldUpdate() {
        assertFalse(mTelephonyAdminReceiver.isCellular2gDisabled());
        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
                true).thenReturn(false);

        mContext.sendBroadcast(new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));
        assertTrue(mTelephonyAdminReceiver.isCellular2gDisabled());

        mContext.sendBroadcast(new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));
        assertFalse(mTelephonyAdminReceiver.isCellular2gDisabled());
        verify(mPhone, times(2)).sendSubscriptionSettings(false);
    }
}