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

Commit 9e73af69 authored by Rambo Wang's avatar Rambo Wang
Browse files

Make carrier service persistent binding more robust

The change introduces an additional checkpoint to make sure
carrier service binding is connected whenever carrier package
has changed to non-null.

When carrier service package (e.g. Google Fi) gets upgraded e.g.
through PlayStore, the persistent binding from telephony to the
carrier service may get released. A rebinding may not perform until
a new trigger event occurs later (e.g. SIM state change, user
unlock or other package installation).

The root cause comes from the race condition when both
CarrierServiceBindHelper and CarrierPrivilegesTracker handle the
package change events. When CSBH firstly received the events and
tried to get current carrier package to rebind, the carrier package
in CPT may not be updated yet, causing the rebinding fail.

To fix the issue, CarrierPrivilegesCallback is introduced into
CSBH which can rebind when reliably knowing carrier package has
been updated.

Bug: 232461097
Test: atest CarrierServiceBindHelperTest
Test: go/carrier-service-binding-sanity
Test: Sanity test on telephony(voice, sms, data connection...)

Change-Id: I09313571239da6a017a02b22aba247c94b45a35d
parent 03083c8f
Loading
Loading
Loading
Loading
+52 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.content.pm.ResolveInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
@@ -47,6 +48,8 @@ import com.android.internal.telephony.util.TelephonyUtils;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Set;

/**
 * Manages long-lived bindings to carrier services
@@ -71,6 +74,8 @@ public class CarrierServiceBindHelper {
    public SparseArray<AppBinding> mBindings = new SparseArray();
    @VisibleForTesting
    public SparseArray<String> mLastSimState = new SparseArray<>();
    // TODO(b/201423849): Clean up PackageChangeReceiver/UserUnlockedReceiver/SIM State change if
    // CarrierServiceChangeCallback can cover the cases
    private final PackageChangeReceiver mPackageMonitor = new CarrierServicePackageMonitor();
    private final LocalLog mLocalLog = new LocalLog(100);

@@ -90,6 +95,30 @@ public class CarrierServiceBindHelper {
        }
    };

    private class CarrierServiceChangeCallback implements
            TelephonyManager.CarrierPrivilegesCallback {
        final int mPhoneId;

        CarrierServiceChangeCallback(int phoneId) {
            this.mPhoneId = phoneId;
        }

        @Override
        public void onCarrierPrivilegesChanged(Set<String> privilegedPackageNames,
                Set<Integer> privilegedUids) {
            // Ignored, not interested here
        }

        @Override
        public void onCarrierServiceChanged(String carrierServicePackageName,
                int carrierServiceUid) {
            logdWithLocalLog("onCarrierServiceChanged, carrierServicePackageName="
                    + carrierServicePackageName + ", carrierServiceUid=" + carrierServiceUid
                    + ", mPhoneId=" + mPhoneId);
            mHandler.sendMessage(mHandler.obtainMessage(EVENT_REBIND, mPhoneId));
        }
    }

    private static final int EVENT_REBIND = 0;
    @VisibleForTesting
    public static final int EVENT_PERFORM_IMMEDIATE_UNBIND = 1;
@@ -123,6 +152,8 @@ public class CarrierServiceBindHelper {
                case EVENT_MULTI_SIM_CONFIG_CHANGED:
                    updateBindingsAndSimStates();
                    break;
                default:
                    Log.e(LOG_TAG, "Unsupported event received: " + msg.what);
            }
        }
    };
@@ -162,6 +193,7 @@ public class CarrierServiceBindHelper {

        // If prevLen > newLen, dispose AppBinding and simState objects.
        for (int phoneId = newLen; phoneId < prevLen; phoneId++) {
            mBindings.get(phoneId).tearDown();
            mBindings.get(phoneId).unbind(true);
            mBindings.delete(phoneId);
            mLastSimState.delete(phoneId);
@@ -193,9 +225,23 @@ public class CarrierServiceBindHelper {
        private String carrierPackage;
        private String carrierServiceClass;
        private long mUnbindScheduledUptimeMillis = -1;
        private final CarrierServiceChangeCallback mCarrierServiceChangeCallback;

        public AppBinding(int phoneId) {
            this.phoneId = phoneId;
            this.mCarrierServiceChangeCallback = new CarrierServiceChangeCallback(phoneId);
            TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
            if (tm != null) {
                tm.registerCarrierPrivilegesCallback(phoneId, new HandlerExecutor(mHandler),
                        mCarrierServiceChangeCallback);
            }
        }

        public void tearDown() {
            TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
            if (tm != null && mCarrierServiceChangeCallback != null) {
                tm.unregisterCarrierPrivilegesCallback(mCarrierServiceChangeCallback);
            }
        }

        public int getPhoneId() {
@@ -365,6 +411,7 @@ public class CarrierServiceBindHelper {
            pw.println("  unbindCount: " + unbindCount);
            pw.println("  lastUnbindMillis: " + lastUnbindMillis);
            pw.println("  mUnbindScheduledUptimeMillis: " + mUnbindScheduledUptimeMillis);
            pw.println("  mCarrierServiceChangeCallback: " + mCarrierServiceChangeCallback);
            pw.println();
        }
    }
@@ -405,27 +452,32 @@ public class CarrierServiceBindHelper {
    private class CarrierServicePackageMonitor extends PackageChangeReceiver {
        @Override
        public void onPackageAdded(String packageName) {
            logdWithLocalLog("onPackageAdded: " + packageName);
            evaluateBinding(packageName, true /* forceUnbind */);
        }

        @Override
        public void onPackageRemoved(String packageName) {
            logdWithLocalLog("onPackageRemoved: " + packageName);
            evaluateBinding(packageName, true /* forceUnbind */);
        }

        @Override
        public void onPackageUpdateFinished(String packageName) {
            logdWithLocalLog("onPackageUpdateFinished: " + packageName);
            evaluateBinding(packageName, true /* forceUnbind */);
        }

        @Override
        public void onPackageModified(String packageName) {
            logdWithLocalLog("onPackageModified: " + packageName);
            evaluateBinding(packageName, false /* forceUnbind */);
        }

        @Override
        public void onHandleForceStop(String[] packages, boolean doit) {
            if (doit) {
                logdWithLocalLog("onHandleForceStop: " + Arrays.toString(packages));
                for (String packageName : packages) {
                    evaluateBinding(packageName, true /* forceUnbind */);
                }
+43 −0
Original line number Diff line number Diff line
@@ -18,10 +18,15 @@ package com.android.internal.telephony;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

import android.os.Message;
import android.telephony.TelephonyManager.CarrierPrivilegesCallback;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -30,10 +35,14 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;

@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class CarrierServiceBindHelperTest extends TelephonyTest {
    private static final int PHONE_ID_0 = 0;
    private static final int PHONE_ID_1 = 1;

    CarrierServiceBindHelper mCarrierServiceBindHelper;
    @Before
    public void setUp() throws Exception {
@@ -90,4 +99,38 @@ public class CarrierServiceBindHelperTest extends TelephonyTest {
                        CarrierServiceBindHelper.EVENT_PERFORM_IMMEDIATE_UNBIND,
                        new Integer(0)));
    }

    @Test
    public void testCarrierPrivilegesCallbackRegistration() {
        // Device starts with DSDS mode
        doReturn(2).when(mTelephonyManager).getActiveModemCount();
        mCarrierServiceBindHelper = new CarrierServiceBindHelper(mContext);
        processAllMessages();

        // Verify that CarrierPrivilegesCallbacks are registered on both phones.
        // Capture the callbacks for further verification
        ArgumentCaptor<CarrierPrivilegesCallback> phone0CallbackCaptor = ArgumentCaptor.forClass(
                CarrierPrivilegesCallback.class);
        verify(mTelephonyManager).registerCarrierPrivilegesCallback(eq(PHONE_ID_0), any(),
                phone0CallbackCaptor.capture());
        CarrierPrivilegesCallback phone0Callback = phone0CallbackCaptor.getAllValues().get(0);
        assertNotNull(phone0Callback);

        ArgumentCaptor<CarrierPrivilegesCallback> phone1CallbackCaptor = ArgumentCaptor.forClass(
                CarrierPrivilegesCallback.class);
        verify(mTelephonyManager).registerCarrierPrivilegesCallback(eq(PHONE_ID_1), any(),
                phone1CallbackCaptor.capture());
        CarrierPrivilegesCallback phone1Callback = phone1CallbackCaptor.getAllValues().get(0);
        assertNotNull(phone1Callback);

        // Switch back to single SIM.
        doReturn(1).when(mTelephonyManager).getActiveModemCount();
        PhoneConfigurationManager.notifyMultiSimConfigChange(1);
        processAllMessages();

        // Verify the callback for phone1 had been unregistered while phone0 didn't.
        verify(mTelephonyManager).unregisterCarrierPrivilegesCallback(eq(phone1Callback));
        verify(mTelephonyManager, never()).unregisterCarrierPrivilegesCallback(eq(phone0Callback));
    }
    // TODO (b/232461097): Add UT cases to cover more scenarios (user unlock, SIM state change...)
}