diff options
3 files changed, 234 insertions, 16 deletions
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java index d40b1b142..bfe30b29d 100644 --- a/src/java/com/android/internal/telephony/InboundSmsHandler.java +++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java @@ -270,7 +270,7 @@ public abstract class InboundSmsHandler extends StateMachine { * This parent state throws an exception (for debug builds) or prints an error for unhandled * message types. */ - class DefaultState extends State { + private class DefaultState extends State { @Override public boolean processMessage(Message msg) { switch (msg.what) { @@ -304,7 +304,7 @@ public abstract class InboundSmsHandler extends StateMachine { * The Startup state waits for {@link SmsBroadcastUndelivered} to process the raw table and * notify the state machine to broadcast any complete PDUs that might not have been broadcast. */ - class StartupState extends State { + private class StartupState extends State { @Override public boolean processMessage(Message msg) { log("StartupState.processMessage:" + msg.what); @@ -333,7 +333,7 @@ public abstract class InboundSmsHandler extends StateMachine { * In the idle state the wakelock is released until a new SM arrives, then we transition * to Delivering mode to handle it, acquiring the wakelock on exit. */ - class IdleState extends State { + private class IdleState extends State { @Override public void enter() { if (DBG) log("entering Idle state"); @@ -389,7 +389,7 @@ public abstract class InboundSmsHandler extends StateMachine { * transition to {@link WaitingState} state to send the ordered broadcast and wait for the * results. When all messages have been processed, the halting state will release the wakelock. */ - class DeliveringState extends State { + private class DeliveringState extends State { @Override public void enter() { if (DBG) log("entering Delivering state"); @@ -462,7 +462,7 @@ public abstract class InboundSmsHandler extends StateMachine { * {@link DeliveringState}, {@link #EVENT_RETURN_TO_IDLE} is sent to transition to * {@link IdleState} after any deferred {@link #EVENT_BROADCAST_SMS} messages are handled. */ - class WaitingState extends State { + private class WaitingState extends State { @Override public boolean processMessage(Message msg) { log("WaitingState.processMessage:" + msg.what); @@ -516,7 +516,7 @@ public abstract class InboundSmsHandler extends StateMachine { * This method is called when a new SMS PDU is injected into application framework. * @param ar is the AsyncResult that has the SMS PDU to be injected. */ - void handleInjectSms(AsyncResult ar) { + private void handleInjectSms(AsyncResult ar) { int result; PendingIntent receivedIntent = null; try { @@ -604,7 +604,7 @@ public abstract class InboundSmsHandler extends StateMachine { * @param result result code indicating any error * @param response callback message sent when operation completes. */ - void notifyAndAcknowledgeLastIncomingSms(boolean success, + private void notifyAndAcknowledgeLastIncomingSms(boolean success, int result, Message response) { if (!success) { // broadcast SMS_REJECTED_ACTION intent @@ -689,7 +689,7 @@ public abstract class InboundSmsHandler extends StateMachine { * @param tracker the tracker containing the message segment to process * @return true if an ordered broadcast was sent; false if waiting for more message segments */ - boolean processMessagePart(InboundSmsTracker tracker) { + private boolean processMessagePart(InboundSmsTracker tracker) { int messageCount = tracker.getMessageCount(); byte[][] pdus; int destPort = tracker.getDestPort(); @@ -954,7 +954,7 @@ public abstract class InboundSmsHandler extends StateMachine { /** * Helper for {@link SmsBroadcastUndelivered} to delete an old message in the raw table. */ - void deleteFromRawTable(String deleteWhere, String[] deleteWhereArgs) { + private void deleteFromRawTable(String deleteWhere, String[] deleteWhereArgs) { int rows = mResolver.delete(sRawUri, deleteWhere, deleteWhereArgs); if (rows == 0) { loge("No rows were deleted from raw table!"); @@ -963,7 +963,7 @@ public abstract class InboundSmsHandler extends StateMachine { } } - Bundle handleSmsWhitelisting(ComponentName target) { + private Bundle handleSmsWhitelisting(ComponentName target) { String pkgName; String reason; if (target != null) { @@ -993,7 +993,7 @@ public abstract class InboundSmsHandler extends StateMachine { * @param destPort the destination port * @param resultReceiver the receiver handling the delivery result */ - void dispatchSmsDeliveryIntent(byte[][] pdus, String format, int destPort, + private void dispatchSmsDeliveryIntent(byte[][] pdus, String format, int destPort, BroadcastReceiver resultReceiver) { Intent intent = new Intent(); intent.putExtra("pdus", pdus); diff --git a/src/java/com/android/internal/telephony/cdma/SmsMessage.java b/src/java/com/android/internal/telephony/cdma/SmsMessage.java index 66b404473..19622dc72 100644 --- a/src/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/src/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -483,7 +483,7 @@ public class SmsMessage extends SmsMessageBase { * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_VMN}, * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WAP} */ - /* package */ int getTeleService() { + public int getTeleService() { return mEnvelope.teleService; } @@ -494,7 +494,7 @@ public class SmsMessage extends SmsMessageBase { * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_BROADCAST}, * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_ACKNOWLEDGE}, */ - /* package */ int getMessageType() { + public int getMessageType() { // NOTE: mEnvelope.messageType is not set correctly for cell broadcasts with some RILs. // Use the service category parameter to detect CMAS and other cell broadcast messages. if (mEnvelope.serviceCategory != 0) { @@ -830,7 +830,7 @@ public class SmsMessage extends SmsMessageBase { * binder-call, and hence should be thread-safe, it has been * synchronized. */ - synchronized static int getNextMessageId() { + public synchronized static int getNextMessageId() { // Testing and dialog with partners has indicated that // msgId==0 is (sometimes?) treated specially by lower levels. // Specifically, the ID is not preserved for delivery ACKs. @@ -1019,7 +1019,7 @@ public class SmsMessage extends SmsMessageBase { /** This function shall be called to get the number of voicemails. * @hide */ - /*package*/ int getNumOfVoicemails() { + public int getNumOfVoicemails() { return mBearerData.numberOfMessages; } @@ -1030,7 +1030,7 @@ public class SmsMessage extends SmsMessageBase { * @return byte array uniquely identifying the message. * @hide */ - /* package */ byte[] getIncomingSmsFingerprint() { + public byte[] getIncomingSmsFingerprint() { ByteArrayOutputStream output = new ByteArrayOutputStream(); output.write(mEnvelope.serviceCategory); diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java new file mode 100644 index 000000000..7417a1cf0 --- /dev/null +++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2015 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.cdma; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncResult; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.IDeviceIdleController; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.provider.Telephony; +import android.telephony.*; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.ContextFixture; +import com.android.internal.telephony.GsmCdmaPhone; +import com.android.internal.telephony.InboundSmsHandler; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.SmsBroadcastUndelivered; +import com.android.internal.telephony.SmsMessageBase; +import com.android.internal.telephony.SmsStorageMonitor; +import com.android.internal.telephony.TelephonyComponentFactory; +import com.android.internal.telephony.TelephonyTestUtils; +import com.android.internal.telephony.cdma.sms.SmsEnvelope; +import com.android.internal.telephony.test.SimulatedCommands; +import com.android.internal.telephony.uicc.UiccController; +import com.android.internal.util.IState; +import com.android.internal.util.StateMachine; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; + +public class CdmaInboundSmsHandlerTest { + private static final String TAG = "CdmaInboundSmsHandlerTest"; + + @Mock + private SmsStorageMonitor mSmsStorageMonitor; + @Mock + private Phone mPhone; + @Mock + private android.telephony.SmsMessage mSmsMessage; + @Mock + private SmsMessage mCdmaSmsMessage; + @Mock + private UiccController mUiccController; + @Mock + private IDeviceIdleController mIDeviceIdleController; + @Mock + private TelephonyComponentFactory mTelephonyComponentFactory; + + private CdmaInboundSmsHandler mCdmaInboundSmsHandler; + private ContextFixture mContextFixture; + private SimulatedCommands mSimulatedCommands; + private TelephonyManager mTelephonyManager; + private SmsEnvelope mSmsEnvelope = new SmsEnvelope(); + + private Object mLock = new Object(); + private boolean mReady; + + private class CdmaInboundSmsHandlerTestHandler extends HandlerThread { + + private CdmaInboundSmsHandlerTestHandler(String name) { + super(name); + } + + @Override + public void onLooperPrepared() { + synchronized (mLock) { + mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler( + mContextFixture.getTestDouble(), mSmsStorageMonitor, mPhone, null); + mReady = true; + } + } + } + + private void waitUntilReady() { + while(true) { + synchronized (mLock) { + if (mReady) { + break; + } + } + } + } + + private IState getCurrentState() { + try { + Method method = StateMachine.class.getDeclaredMethod("getCurrentState"); + method.setAccessible(true); + return (IState) method.invoke(mCdmaInboundSmsHandler); + } catch (Exception e) { + fail(e.toString()); + return null; + } + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + //Use reflection to mock singletons + Field field = UiccController.class.getDeclaredField("mInstance"); + field.setAccessible(true); + field.set(null, mUiccController); + + field = TelephonyComponentFactory.class.getDeclaredField("sInstance"); + field.setAccessible(true); + field.set(null, mTelephonyComponentFactory); + + field = SmsMessage.class.getDeclaredField("mEnvelope"); + field.setAccessible(true); + field.set(mCdmaSmsMessage, mSmsEnvelope); + + mContextFixture = new ContextFixture(); + mSimulatedCommands = new SimulatedCommands(); + mPhone.mCi = mSimulatedCommands; + + mTelephonyManager = TelephonyManager.from(mContextFixture.getTestDouble()); + doReturn(true).when(mTelephonyManager).getSmsReceiveCapableForPhone(anyInt(), anyBoolean()); + doReturn(true).when(mSmsStorageMonitor).isStorageAvailable(); + doReturn(mIDeviceIdleController).when(mTelephonyComponentFactory). + getIDeviceIdleController(); + + mReady = false; + new CdmaInboundSmsHandlerTestHandler(TAG).start(); + waitUntilReady(); + } + + @After + public void tearDown() throws Exception { + mCdmaInboundSmsHandler = null; + } + + @Test @SmallTest + public void testNewSms() { + // verify initially in StartupState + assertEquals("StartupState", getCurrentState().getName()); + + // start SmsBroadcastUndelivered thread to trigger transition to IdleState + Thread broadcastThread = new Thread(new SmsBroadcastUndelivered( + mContextFixture.getTestDouble(), null, mCdmaInboundSmsHandler)); + broadcastThread.start(); + TelephonyTestUtils.waitForMs(50); + + assertEquals("IdleState", getCurrentState().getName()); + + // send new SMS to state machine and verify that triggers SMS_DELIVER_ACTION + byte[] smsPdu = new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF}; + mSmsMessage.mWrappedSmsMessage = mCdmaSmsMessage; + doReturn(smsPdu).when(mCdmaSmsMessage).getPdu(); + doReturn(SmsEnvelope.TELESERVICE_WMT).when(mCdmaSmsMessage).getTeleService(); + mCdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, + new AsyncResult(null, mSmsMessage, null)); + TelephonyTestUtils.waitForMs(100); + + ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mContextFixture.getTestDouble(), times(2)). + sendBroadcast(intentArgumentCaptor.capture()); + + List<Intent> list = intentArgumentCaptor.getAllValues(); + /* logd("list.size() " + list.size()); + for (int i = 0; i < list.size(); i++) { + logd("list.get(i) " + list.get(i)); + } */ + //todo: seems to be some issue with ArgumentCaptor. Both DELIVER and RECEIVED broadcasts + //can be seen in logs but according to list both are RECEIVED + //assertEquals(Telephony.Sms.Intents.SMS_DELIVER_ACTION, + // list.get(0).getAction()); + boolean smsReceivedAction = false; + for (Intent i : list) { + if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(i.getAction())) { + smsReceivedAction = true; + break; + } + } + assertTrue(smsReceivedAction); + + assertEquals("IdleState", getCurrentState().getName()); + } + + private static void logd(String s) { + Log.d(TAG, s); + } +} |