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

Commit ae91ab32 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Timeout if carrier service does not respond." into sc-dev

parents b9c6e0c2 16213446
Loading
Loading
Loading
Loading
+54 −3
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.os.AsyncResult;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.Process;
@@ -63,6 +64,8 @@ import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -80,6 +83,8 @@ import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccController;
import com.android.telephony.Rlog;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -149,6 +154,7 @@ public abstract class SMSDispatcher extends Handler {
    protected final CommandsInterface mCi;
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    protected final TelephonyManager mTelephonyManager;
    protected final LocalLog mLocalLog = new LocalLog(16);

    /** Maximum number of times to retry sending a failed SMS. */
    private static final int MAX_SEND_RETRIES = 3;
@@ -174,6 +180,9 @@ public abstract class SMSDispatcher extends Handler {
    protected boolean mSmsCapable = true;
    protected boolean mSmsSendDisabled;

    @VisibleForTesting
    public int mCarrierMessagingTimeout = 10 * 60 * 1000; //10 minutes

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    protected static int getNextConcatenatedRef() {
        sConcatenatedRef += 1;
@@ -344,19 +353,23 @@ public abstract class SMSDispatcher extends Handler {
    /**
     * Use the carrier messaging service to send a data or text SMS.
     */
    protected abstract class SmsSender {
    protected abstract class SmsSender extends Handler {
        private static final int EVENT_TIMEOUT = 1;
        protected final SmsTracker mTracker;
        // Initialized in sendSmsByCarrierApp
        protected volatile SmsSenderCallback mSenderCallback;
        protected final CarrierMessagingServiceWrapper mCarrierMessagingServiceWrapper =
                new CarrierMessagingServiceWrapper();
        private String mCarrierPackageName;

        protected SmsSender(SmsTracker tracker) {
            super(Looper.getMainLooper());
            mTracker = tracker;
        }

        public void sendSmsByCarrierApp(String carrierPackageName,
                                        SmsSenderCallback senderCallback) {
            mCarrierPackageName = carrierPackageName;
            mSenderCallback = senderCallback;
            if (!mCarrierMessagingServiceWrapper.bindToCarrierMessagingService(
                    mContext, carrierPackageName, runnable -> runnable.run(),
@@ -367,10 +380,33 @@ public abstract class SMSDispatcher extends Handler {
                        0 /* messageRef */);
            } else {
                Rlog.d(TAG, "bindService() for carrier messaging service succeeded");
                sendMessageDelayed(obtainMessage(EVENT_TIMEOUT), mCarrierMessagingTimeout);
            }
        }

        public abstract void onServiceReady();

        @Override
        public void handleMessage(Message msg) {
            if (msg.what == EVENT_TIMEOUT) {
                logWithLocalLog("handleMessage: did not receive response from "
                        + mCarrierPackageName + " for " + mCarrierMessagingTimeout + " ms");
                mSenderCallback.onSendSmsComplete(
                        CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
                        0 /* messageRef */);
            } else {
                logWithLocalLog("handleMessage: received unexpected message " + msg.what);
            }
        }

        public void removeTimeout() {
            removeMessages(EVENT_TIMEOUT);
        }
    }

    private void logWithLocalLog(String logStr) {
        mLocalLog.log(logStr);
        Rlog.d(TAG, logStr);
    }

    /**
@@ -398,7 +434,7 @@ public abstract class SMSDispatcher extends Handler {
                            runnable -> runnable.run(),
                            mSenderCallback);
                } catch (RuntimeException e) {
                    Rlog.e(TAG, "Exception sending the SMS: " + e);
                    Rlog.e(TAG, "Exception sending the SMS: " + e.getMessage());
                    mSenderCallback.onSendSmsComplete(
                            CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
                            0 /* messageRef */);
@@ -472,6 +508,7 @@ public abstract class SMSDispatcher extends Handler {
            try {
                mSmsSender.mCarrierMessagingServiceWrapper.disconnect();
                processSendSmsResponse(mSmsSender.mTracker, result, messageRef);
                mSmsSender.removeTimeout();
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
@@ -2476,4 +2513,18 @@ public abstract class SMSDispatcher extends Handler {
            Binder.restoreCallingIdentity(token);
        }
    }

    /**
     * Dump local logs
     */
    public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
        pw.println(TAG);
        pw.increaseIndent();
        pw.println("mLocalLog:");
        pw.increaseIndent();
        mLocalLog.dump(fd, pw, args);
        pw.decreaseIndent();
        pw.decreaseIndent();
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -1051,6 +1051,9 @@ public class SmsDispatchersController extends Handler {
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        mGsmInboundSmsHandler.dump(fd, pw, args);
        mCdmaInboundSmsHandler.dump(fd, pw, args);
        mGsmDispatcher.dump(fd, pw, args);
        mCdmaDispatcher.dump(fd, pw, args);
        mImsSmsDispatcher.dump(fd, pw, args);
    }

    private void logd(String msg) {
+13 −3
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

/**
 * Controls a test {@link Context} as would be provided by the Android framework to an
@@ -197,6 +198,9 @@ public class ContextFixture implements TestFixture<Context> {
                Intent serviceIntent,
                ServiceConnection connection,
                int flags) {
            if (mMockBindingFailureForPackage.contains(serviceIntent.getPackage())) {
                return false;
            }
            if (mServiceByServiceConnection.containsKey(connection)) {
                throw new RuntimeException("ServiceConnection already bound: " + connection);
            }
@@ -218,10 +222,11 @@ public class ContextFixture implements TestFixture<Context> {
        public void unbindService(
                ServiceConnection connection) {
            IInterface service = mServiceByServiceConnection.remove(connection);
            if (service == null) {
                throw new RuntimeException("ServiceConnection not found: " + connection);
            }
            if (service != null) {
                connection.onServiceDisconnected(mComponentNameByService.get(service));
            } else {
                logd("unbindService: ServiceConnection not found: " + connection);
            }
        }

        @Override
@@ -624,6 +629,7 @@ public class ContextFixture implements TestFixture<Context> {
    private final Map<ComponentName, IntentFilter> mIntentFilterByComponentName = new HashMap<>();
    private final Map<IInterface, ComponentName> mComponentNameByService =
            new HashMap<IInterface, ComponentName>();
    private final Set<String> mMockBindingFailureForPackage = new HashSet();
    private final Map<ServiceConnection, IInterface> mServiceByServiceConnection =
            new HashMap<ServiceConnection, IInterface>();
    private final Multimap<String, BroadcastReceiver> mBroadcastReceiversByAction =
@@ -791,6 +797,10 @@ public class ContextFixture implements TestFixture<Context> {
        mComponentNameByService.put(service, name);
    }

    public void mockBindingFailureForPackage(String packageName) {
        mMockBindingFailureForPackage.add(packageName);
    }

    private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) {
        List<ResolveInfo> result = new ArrayList<ResolveInfo>();
        for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) {
+1 −0
Original line number Diff line number Diff line
@@ -589,6 +589,7 @@ public abstract class TelephonyTest {
                eq(UiccController.APP_FAM_3GPP2));
        doReturn(mUiccCardApplicationIms).when(mUiccController).getUiccCardApplication(anyInt(),
                eq(UiccController.APP_FAM_IMS));
        doReturn(mUiccCard).when(mUiccController).getUiccCard(anyInt());

        doAnswer(new Answer<IccRecords>() {
            public IccRecords answer(InvocationOnMock invocation) {
+139 −18
Original line number Diff line number Diff line
@@ -23,32 +23,44 @@ import static com.android.internal.telephony.SmsUsageMonitor.PREMIUM_SMS_PERMISS
import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.location.Country;
import android.location.CountryDetector;
import android.os.HandlerThread;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.Settings;
import android.service.carrier.CarrierMessagingService;
import android.service.carrier.ICarrierMessagingCallback;
import android.service.carrier.ICarrierMessagingService;
import android.telephony.SmsManager;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Singleton;

import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;

import com.android.internal.telephony.ContextFixture;
import com.android.internal.telephony.ISub;
@@ -62,15 +74,22 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class GsmSmsDispatcherTest extends TelephonyTest {

    private static final long TIMEOUT_MS = 500;
    private static final String CARRIER_APP_PACKAGE_NAME = "com.android.carrier";

    @Mock
    private android.telephony.SmsMessage mSmsMessage;
@@ -86,6 +105,9 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
    private SMSDispatcher.SmsTracker mSmsTracker;
    @Mock
    private ISub.Stub mISubStub;
    @Mock
    private ICarrierMessagingService.Stub mICarrierAppMessagingService;

    private Object mLock = new Object();
    private boolean mReceivedTestIntent;
    private static final String TEST_INTENT = "com.android.internal.telephony.TEST_INTENT";
@@ -95,6 +117,7 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
            logd("onReceive");
            synchronized (mLock) {
                mReceivedTestIntent = true;
                mLock.notifyAll();
            }
        }
    };
@@ -129,6 +152,9 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
        mGsmSmsDispatcherTestHandler = new GsmSmsDispatcherTestHandler(getClass().getSimpleName());
        mGsmSmsDispatcherTestHandler.start();
        waitUntilReady();
        mGsmSmsDispatcher = new GsmSMSDispatcher(mPhone, mSmsDispatchersController,
                mGsmInboundSmsHandler);
        processAllMessages();
    }

    @After
@@ -142,7 +168,7 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
    @Test @SmallTest
    public void testSmsStatus() {
        mSimulatedCommands.notifySmsStatus(new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF});
        TelephonyTestUtils.waitForMs(50);
        processAllMessages();
        verify(mSimulatedCommandsVerifier).acknowledgeLastIncomingGsmSms(true, 0, null);
    }

@@ -202,18 +228,22 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
        }
    }

    @Test
    @SmallTest
    @FlakyTest
    @Ignore
    public void testSendTextWithInvalidDestAddr() throws Exception {
    private void registerTestIntentReceiver() throws Exception {
        // unmock ActivityManager to be able to register receiver, create real PendingIntent and
        // receive TEST_INTENT
        restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
        restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
        Context realContext = TestApplication.getAppContext();
        realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT));
        PendingIntent pendingIntent = PendingIntent.getBroadcast(realContext, 0,
    }

    @Test
    @SmallTest
    @FlakyTest
    @Ignore
    public void testSendTextWithInvalidDestAddr() throws Exception {
        registerTestIntentReceiver();
        PendingIntent pendingIntent = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
                new Intent(TEST_INTENT), 0);
        // send invalid dest address: +
        mReceivedTestIntent = false;
@@ -250,7 +280,8 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
                Settings.Global.DEVICE_PROVISIONED, 1);

        mGsmSmsDispatcher.sendRawPdu(new SMSDispatcher.SmsTracker[] {mSmsTracker});
        waitForHandlerAction(mGsmSmsDispatcher, TIMEOUT_MS);
        //waitForHandlerAction(mGsmSmsDispatcher, TIMEOUT_MS);
        processAllMessages();

        verify(mSmsUsageMonitor, times(1)).checkDestination(any(), any());
        verify(mSmsUsageMonitor, times(1)).getPremiumSmsPermission(any());
@@ -264,13 +295,7 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
    @FlakyTest
    @Ignore
    public void testSendMultipartTextWithInvalidText() throws Exception {
        // unmock ActivityManager to be able to register receiver, create real PendingIntent and
        // receive TEST_INTENT
        restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
        restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);

        Context realContext = TestApplication.getAppContext();
        realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT));
        registerTestIntentReceiver();

        // initiate parameters for an invalid text MO SMS (the 2nd segmeant has 161 characters)
        ArrayList<String> parts = new ArrayList<>();
@@ -280,7 +305,7 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
                + "8");

        ArrayList<PendingIntent> sentIntents = new ArrayList<>();
        PendingIntent sentIntent = PendingIntent.getBroadcast(realContext, 0,
        PendingIntent sentIntent = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
                new Intent(TEST_INTENT), PendingIntent.FLAG_IMMUTABLE);
        sentIntents.add(sentIntent);
        sentIntents.add(sentIntent);
@@ -296,4 +321,100 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
            assertEquals(SmsManager.RESULT_ERROR_GENERIC_FAILURE, mTestReceiver.getResultCode());
        }
    }

    private void mockCarrierApp()
            throws RemoteException {
        mContextFixture.addService(
                CarrierMessagingService.SERVICE_INTERFACE,
                new ComponentName(CARRIER_APP_PACKAGE_NAME, "CarrierAppFilterClass"),
                CARRIER_APP_PACKAGE_NAME,
                mICarrierAppMessagingService,
                new ServiceInfo());
        when(mICarrierAppMessagingService.asBinder()).thenReturn(mICarrierAppMessagingService);
        mockUiccWithCarrierApp();
    }

    private void mockUiccWithCarrierApp() {
        when(mUiccController.getUiccCard(mPhone.getPhoneId())).thenReturn(mUiccCard);
        List<String> carrierPackages = new ArrayList<>();
        carrierPackages.add(CARRIER_APP_PACKAGE_NAME);
        when(mUiccCard.getCarrierPackageNamesForIntent(
                any(PackageManager.class), any(Intent.class))).thenReturn(carrierPackages);
    }

    private void mockCarrierAppStubResults(final int result, ICarrierMessagingService.Stub stub,
            boolean callOnFilterComplete)
            throws RemoteException {
        when(stub.queryLocalInterface(anyString())).thenReturn(stub);
        when(stub.asBinder()).thenReturn(stub);
        doAnswer(new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocation) throws Throwable {
                Object[] args = invocation.getArguments();
                ICarrierMessagingCallback callback = (ICarrierMessagingCallback) args[4];
                if (callOnFilterComplete) {
                    callback.onSendSmsComplete(result, 0);
                }
                return null;
            }
        }).when(stub).sendTextSms(
                anyString(), anyInt(), anyString(), anyInt(),
                any(ICarrierMessagingCallback.class));
    }

    @Test
    @SmallTest
    public void testSendSmsByCarrierApp() throws Exception {
        mockCarrierApp();
        mockCarrierAppStubResults(CarrierMessagingService.SEND_STATUS_OK,
                mICarrierAppMessagingService, true);
        registerTestIntentReceiver();

        PendingIntent pendingIntent = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
                new Intent(TEST_INTENT), PendingIntent.FLAG_MUTABLE);
        mReceivedTestIntent = false;

        mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms",
                pendingIntent, null, null, null, false, -1, false, -1, false, 0L);
        processAllMessages();
        synchronized (mLock) {
            if (!mReceivedTestIntent) {
                // long wait since sometimes broadcasts can take a long time if the system is loaded
                mLock.wait(60000);
            }
            assertEquals(true, mReceivedTestIntent);
            int resultCode = mTestReceiver.getResultCode();
            assertTrue("Unexpected result code: " + resultCode,
                    resultCode == SmsManager.RESULT_ERROR_NONE || resultCode == Activity.RESULT_OK);
            verify(mSimulatedCommandsVerifier, times(0)).sendSMS(anyString(), anyString(),
                    any(Message.class));
        }
    }

    @Test
    @SmallTest
    public void testSendSmsByCarrierAppNoResponse() throws Exception {
        mockCarrierApp();
        // do not mock result, instead reduce the timeout for test
        mGsmSmsDispatcher.mCarrierMessagingTimeout = 100;

        mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms",
                null, null, null, null, false, -1, false, -1, false, 0L);
        // wait for timeout
        waitForMs(150);
        verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(), any(Message.class));
    }

    @Test
    @SmallTest
    public void testSendSmsByCarrierAppBindingFailed() throws Exception {
        mContextFixture.mockBindingFailureForPackage(CARRIER_APP_PACKAGE_NAME);
        // mock presence of carrier app, but do not create a mock service to make binding fail
        mockUiccWithCarrierApp();

        mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms",
                null, null, null, null, false, -1, false, -1, false, 0L);
        processAllMessages();
        verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(), any(Message.class));
    }
}