[Mainline] move ServiceStateProvider to TeleService
-add ServiceStateProvider.java from TelephonyProvider
-add ServiceStateProviderTest.java from TelephonyProvider
-modify client/AndroidManifest.xml
-modify tests/Android.bp
Bug: 144486008
Test: atest ServiceStateProviderTest (PASS)
Change-Id: Id51aee376bf519b0a431f5f5caaea99e23fb669b
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7313adb..00fcbe8 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -651,5 +651,13 @@
<category android:name="android.intent.category.VOICE_LAUNCH" />
</intent-filter>
</activity>
+
+ <provider
+ android:name="ServiceStateProvider"
+ android:authorities="service-state"
+ android:exported="true"
+ android:multiprocess="false"
+ android:singleUser="true"
+ android:writePermission="android.permission.MODIFY_PHONE_STATE"/>
</application>
</manifest>
diff --git a/src/com/android/phone/ServiceStateProvider.java b/src/com/android/phone/ServiceStateProvider.java
new file mode 100644
index 0000000..69ea27c
--- /dev/null
+++ b/src/com/android/phone/ServiceStateProvider.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2019 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 android.provider.Telephony.ServiceStateTable;
+import static android.provider.Telephony.ServiceStateTable.CDMA_DEFAULT_ROAMING_INDICATOR;
+import static android.provider.Telephony.ServiceStateTable.CDMA_ERI_ICON_INDEX;
+import static android.provider.Telephony.ServiceStateTable.CDMA_ERI_ICON_MODE;
+import static android.provider.Telephony.ServiceStateTable.CDMA_ROAMING_INDICATOR;
+import static android.provider.Telephony.ServiceStateTable.CONTENT_URI;
+import static android.provider.Telephony.ServiceStateTable.CSS_INDICATOR;
+import static android.provider.Telephony.ServiceStateTable.DATA_OPERATOR_ALPHA_LONG;
+import static android.provider.Telephony.ServiceStateTable.DATA_OPERATOR_ALPHA_SHORT;
+import static android.provider.Telephony.ServiceStateTable.DATA_OPERATOR_NUMERIC;
+import static android.provider.Telephony.ServiceStateTable.DATA_REG_STATE;
+import static android.provider.Telephony.ServiceStateTable.DATA_ROAMING_TYPE;
+import static android.provider.Telephony.ServiceStateTable.IS_EMERGENCY_ONLY;
+import static android.provider.Telephony.ServiceStateTable.IS_MANUAL_NETWORK_SELECTION;
+import static android.provider.Telephony.ServiceStateTable.IS_USING_CARRIER_AGGREGATION;
+import static android.provider.Telephony.ServiceStateTable.NETWORK_ID;
+import static android.provider.Telephony.ServiceStateTable.OPERATOR_ALPHA_LONG_RAW;
+import static android.provider.Telephony.ServiceStateTable.OPERATOR_ALPHA_SHORT_RAW;
+import static android.provider.Telephony.ServiceStateTable.RIL_DATA_RADIO_TECHNOLOGY;
+import static android.provider.Telephony.ServiceStateTable.RIL_VOICE_RADIO_TECHNOLOGY;
+import static android.provider.Telephony.ServiceStateTable.SERVICE_STATE;
+import static android.provider.Telephony.ServiceStateTable.SYSTEM_ID;
+import static android.provider.Telephony.ServiceStateTable.VOICE_OPERATOR_ALPHA_LONG;
+import static android.provider.Telephony.ServiceStateTable.VOICE_OPERATOR_ALPHA_SHORT;
+import static android.provider.Telephony.ServiceStateTable.VOICE_OPERATOR_NUMERIC;
+import static android.provider.Telephony.ServiceStateTable.VOICE_REG_STATE;
+import static android.provider.Telephony.ServiceStateTable.VOICE_ROAMING_TYPE;
+import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
+import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionIdAndField;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
+import android.net.Uri;
+import android.os.Parcel;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashMap;
+
+/**
+ * The class to provide base facility to access ServiceState related content,
+ * which is stored in a SQLite database.
+ */
+public class ServiceStateProvider extends ContentProvider {
+ private static final String TAG = "ServiceStateProvider";
+
+ public static final String AUTHORITY = ServiceStateTable.AUTHORITY;
+ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+ private final HashMap<Integer, ServiceState> mServiceStates = new HashMap<>();
+ private static final String[] sColumns = {
+ VOICE_REG_STATE,
+ DATA_REG_STATE,
+ VOICE_ROAMING_TYPE,
+ DATA_ROAMING_TYPE,
+ VOICE_OPERATOR_ALPHA_LONG,
+ VOICE_OPERATOR_ALPHA_SHORT,
+ VOICE_OPERATOR_NUMERIC,
+ DATA_OPERATOR_ALPHA_LONG,
+ DATA_OPERATOR_ALPHA_SHORT,
+ DATA_OPERATOR_NUMERIC,
+ IS_MANUAL_NETWORK_SELECTION,
+ RIL_VOICE_RADIO_TECHNOLOGY,
+ RIL_DATA_RADIO_TECHNOLOGY,
+ CSS_INDICATOR,
+ NETWORK_ID,
+ SYSTEM_ID,
+ CDMA_ROAMING_INDICATOR,
+ CDMA_DEFAULT_ROAMING_INDICATOR,
+ CDMA_ERI_ICON_INDEX,
+ CDMA_ERI_ICON_MODE,
+ IS_EMERGENCY_ONLY,
+ IS_USING_CARRIER_AGGREGATION,
+ OPERATOR_ALPHA_LONG_RAW,
+ OPERATOR_ALPHA_SHORT_RAW,
+ };
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ /**
+ * Returns the {@link ServiceState} information on specified subscription.
+ *
+ * @param subId whose subscriber id is returned
+ * @return the {@link ServiceState} information on specified subscription.
+ */
+ @VisibleForTesting
+ public ServiceState getServiceState(int subId) {
+ return mServiceStates.get(subId);
+ }
+
+ /**
+ * Returns the system's default subscription id.
+ *
+ * @return the "system" default subscription id.
+ */
+ @VisibleForTesting
+ public int getDefaultSubId() {
+ return SubscriptionManager.getDefaultSubscriptionId();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ if (uri.isPathPrefixMatch(CONTENT_URI)) {
+ // Parse the subId
+ int subId = 0;
+ try {
+ subId = Integer.parseInt(uri.getLastPathSegment());
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "insert: no subId provided in uri");
+ throw e;
+ }
+ Log.d(TAG, "subId=" + subId);
+
+ // handle DEFAULT_SUBSCRIPTION_ID
+ if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ subId = getDefaultSubId();
+ }
+
+ final Parcel p = Parcel.obtain();
+ final byte[] rawBytes = values.getAsByteArray(SERVICE_STATE);
+ p.unmarshall(rawBytes, 0, rawBytes.length);
+ p.setDataPosition(0);
+
+ // create the new service state
+ final ServiceState newSS = ServiceState.CREATOR.createFromParcel(p);
+
+ // notify listeners
+ // if ss is null (e.g. first service state update) we will notify for all fields
+ ServiceState ss = getServiceState(subId);
+ notifyChangeForSubIdAndField(getContext(), ss, newSS, subId);
+ notifyChangeForSubId(getContext(), ss, newSS, subId);
+
+ // store the new service state
+ mServiceStates.put(subId, newSS);
+ return uri;
+ }
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new RuntimeException("Not supported");
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new RuntimeException("Not supported");
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new RuntimeException("Not supported");
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ if (!uri.isPathPrefixMatch(CONTENT_URI)) {
+ throw new IllegalArgumentException("Invalid URI: " + uri);
+ } else {
+ // Parse the subId
+ int subId = 0;
+ try {
+ subId = Integer.parseInt(uri.getLastPathSegment());
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "query: no subId provided in uri, using default.");
+ subId = getDefaultSubId();
+ }
+ Log.d(TAG, "subId=" + subId);
+
+ // handle DEFAULT_SUBSCRIPTION_ID
+ if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ subId = getDefaultSubId();
+ }
+
+ // Get the service state
+ ServiceState ss = getServiceState(subId);
+ if (ss == null) {
+ Log.d(TAG, "returning null");
+ return null;
+ }
+
+ // Build the result
+ final int voice_reg_state = ss.getVoiceRegState();
+ final int data_reg_state = ss.getDataRegState();
+ final int voice_roaming_type = ss.getVoiceRoamingType();
+ final int data_roaming_type = ss.getDataRoamingType();
+ final String voice_operator_alpha_long = ss.getOperatorAlphaLong();
+ final String voice_operator_alpha_short = ss.getOperatorAlphaShort();
+ final String voice_operator_numeric = ss.getOperatorNumeric();
+ final String data_operator_alpha_long = ss.getOperatorAlphaLong();
+ final String data_operator_alpha_short = ss.getOperatorAlphaShort();
+ final String data_operator_numeric = ss.getOperatorNumeric();
+ final int is_manual_network_selection = (ss.getIsManualSelection()) ? 1 : 0;
+ final int ril_voice_radio_technology = ss.getRilVoiceRadioTechnology();
+ final int ril_data_radio_technology = ss.getRilDataRadioTechnology();
+ final int css_indicator = ss.getCssIndicator();
+ final int network_id = ss.getCdmaNetworkId();
+ final int system_id = ss.getCdmaSystemId();
+ final int cdma_roaming_indicator = ss.getCdmaRoamingIndicator();
+ final int cdma_default_roaming_indicator = ss.getCdmaDefaultRoamingIndicator();
+ final int cdma_eri_icon_index = ss.getCdmaEriIconIndex();
+ final int cdma_eri_icon_mode = ss.getCdmaEriIconMode();
+ final int is_emergency_only = (ss.isEmergencyOnly()) ? 1 : 0;
+ final int is_using_carrier_aggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
+ final String operator_alpha_long_raw = ss.getOperatorAlphaLongRaw();
+ final String operator_alpha_short_raw = ss.getOperatorAlphaShortRaw();
+
+ return buildSingleRowResult(projection, sColumns, new Object[] {
+ voice_reg_state,
+ data_reg_state,
+ voice_roaming_type,
+ data_roaming_type,
+ voice_operator_alpha_long,
+ voice_operator_alpha_short,
+ voice_operator_numeric,
+ data_operator_alpha_long,
+ data_operator_alpha_short,
+ data_operator_numeric,
+ is_manual_network_selection,
+ ril_voice_radio_technology,
+ ril_data_radio_technology,
+ css_indicator,
+ network_id,
+ system_id,
+ cdma_roaming_indicator,
+ cdma_default_roaming_indicator,
+ cdma_eri_icon_index,
+ cdma_eri_icon_mode,
+ is_emergency_only,
+ is_using_carrier_aggregation,
+ operator_alpha_long_raw,
+ operator_alpha_short_raw,
+ });
+ }
+ }
+
+ private static Cursor buildSingleRowResult(String[] projection, String[] availableColumns,
+ Object[] data) {
+ if (projection == null) {
+ projection = availableColumns;
+ }
+ final MatrixCursor c = new MatrixCursor(projection, 1);
+ final RowBuilder row = c.newRow();
+ for (int i = 0; i < c.getColumnCount(); i++) {
+ final String columnName = c.getColumnName(i);
+ boolean found = false;
+ for (int j = 0; j < availableColumns.length; j++) {
+ if (availableColumns[j].equals(columnName)) {
+ row.add(data[j]);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new IllegalArgumentException("Invalid column " + projection[i]);
+ }
+ }
+ return c;
+ }
+
+ /**
+ * Notify interested apps that certain fields of the ServiceState have changed.
+ *
+ * Apps which want to wake when specific fields change can use
+ * JobScheduler's TriggerContentUri. This replaces the waking functionality of the implicit
+ * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O.
+ *
+ * We will only notify for certain fields. This is an intentional change from the behavior of
+ * the broadcast. Listeners will be notified when the voice or data registration state or
+ * roaming type changes.
+ */
+ @VisibleForTesting
+ public static void notifyChangeForSubIdAndField(Context context, ServiceState oldSS,
+ ServiceState newSS, int subId) {
+ final boolean firstUpdate = (oldSS == null) ? true : false;
+
+ // for every field, if the field has changed values, notify via the provider
+ if (firstUpdate || voiceRegStateChanged(oldSS, newSS)) {
+ context.getContentResolver().notifyChange(
+ getUriForSubscriptionIdAndField(subId, VOICE_REG_STATE),
+ /* observer= */ null, /* syncToNetwork= */ false);
+ }
+ if (firstUpdate || dataRegStateChanged(oldSS, newSS)) {
+ context.getContentResolver().notifyChange(
+ getUriForSubscriptionIdAndField(subId, DATA_REG_STATE), null, false);
+ }
+ if (firstUpdate || voiceRoamingTypeChanged(oldSS, newSS)) {
+ context.getContentResolver().notifyChange(
+ getUriForSubscriptionIdAndField(subId, VOICE_ROAMING_TYPE), null, false);
+ }
+ if (firstUpdate || dataRoamingTypeChanged(oldSS, newSS)) {
+ context.getContentResolver().notifyChange(
+ getUriForSubscriptionIdAndField(subId, DATA_ROAMING_TYPE), null, false);
+ }
+ }
+
+ private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) {
+ return oldSS.getVoiceRegState() != newSS.getVoiceRegState();
+ }
+
+ private static boolean dataRegStateChanged(ServiceState oldSS, ServiceState newSS) {
+ return oldSS.getDataRegState() != newSS.getDataRegState();
+ }
+
+ private static boolean voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
+ return oldSS.getVoiceRoamingType() != newSS.getVoiceRoamingType();
+ }
+
+ private static boolean dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
+ return oldSS.getDataRoamingType() != newSS.getDataRoamingType();
+ }
+
+ /**
+ * Notify interested apps that the ServiceState has changed.
+ *
+ * Apps which want to wake when any field in the ServiceState has changed can use
+ * JobScheduler's TriggerContentUri. This replaces the waking functionality of the implicit
+ * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O.
+ *
+ * We will only notify for certain fields. This is an intentional change from the behavior of
+ * the broadcast. Listeners will only be notified when the voice/data registration state or
+ * roaming type changes.
+ */
+ @VisibleForTesting
+ public static void notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS,
+ int subId) {
+ // if the voice or data registration or roaming state field has changed values, notify via
+ // the provider.
+ // If oldSS is null and newSS is not (e.g. first update of service state) this will also
+ // notify
+ if (oldSS == null || voiceRegStateChanged(oldSS, newSS) || dataRegStateChanged(oldSS, newSS)
+ || voiceRoamingTypeChanged(oldSS, newSS) || dataRoamingTypeChanged(oldSS, newSS)) {
+ context.getContentResolver().notifyChange(getUriForSubscriptionId(subId), null, false);
+ }
+ }
+}
diff --git a/tests/Android.bp b/tests/Android.bp
index 22b40b5..7ed234e 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -25,6 +25,7 @@
"telephony-common",
"android.test.base",
"ims-common",
+ "android.test.mock",
],
platform_apis: true,
certificate: "platform",
diff --git a/tests/src/com/android/phone/ServiceStateProviderTest.java b/tests/src/com/android/phone/ServiceStateProviderTest.java
new file mode 100644
index 0000000..5f13389
--- /dev/null
+++ b/tests/src/com/android/phone/ServiceStateProviderTest.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2019 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 android.provider.Telephony.ServiceStateTable;
+import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.content.pm.ProviderInfo;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for simple queries of ServiceStateProvider.
+ *
+ * Build, install and run the tests by running the commands below:
+ * runtest --path <dir or file>
+ * runtest --path <dir or file> --test-method <testMethodName>
+ * e.g.)
+ * runtest --path tests/src/com/android/phone/ServiceStateProviderTest.java \
+ * --test-method testGetServiceState
+ */
+public class ServiceStateProviderTest {
+ private static final String TAG = "ServiceStateProviderTest";
+
+ private Context mContext;
+ private MockContentResolver mContentResolver;
+ private ServiceState mTestServiceState;
+ private ServiceState mTestServiceStateForSubId1;
+
+ private final String[] mTestProjection =
+ {
+ ServiceStateTable.VOICE_REG_STATE,
+ ServiceStateTable.DATA_REG_STATE,
+ ServiceStateTable.VOICE_OPERATOR_ALPHA_LONG,
+ ServiceStateTable.VOICE_OPERATOR_ALPHA_SHORT,
+ ServiceStateTable.VOICE_OPERATOR_NUMERIC,
+ ServiceStateTable.DATA_OPERATOR_ALPHA_LONG,
+ ServiceStateTable.DATA_OPERATOR_ALPHA_SHORT,
+ ServiceStateTable.DATA_OPERATOR_NUMERIC,
+ ServiceStateTable.IS_MANUAL_NETWORK_SELECTION,
+ ServiceStateTable.RIL_VOICE_RADIO_TECHNOLOGY,
+ ServiceStateTable.RIL_DATA_RADIO_TECHNOLOGY,
+ ServiceStateTable.CSS_INDICATOR,
+ ServiceStateTable.NETWORK_ID,
+ ServiceStateTable.SYSTEM_ID,
+ ServiceStateTable.CDMA_ROAMING_INDICATOR,
+ ServiceStateTable.CDMA_DEFAULT_ROAMING_INDICATOR,
+ ServiceStateTable.CDMA_ERI_ICON_INDEX,
+ ServiceStateTable.CDMA_ERI_ICON_MODE,
+ ServiceStateTable.IS_EMERGENCY_ONLY,
+ ServiceStateTable.IS_USING_CARRIER_AGGREGATION,
+ ServiceStateTable.OPERATOR_ALPHA_LONG_RAW,
+ ServiceStateTable.OPERATOR_ALPHA_SHORT_RAW,
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = mock(Context.class);
+ mContentResolver = new MockContentResolver() {
+ @Override
+ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+ throw new RuntimeException("notifyChange!");
+ }
+ };
+ doReturn(mContentResolver).when(mContext).getContentResolver();
+
+ mTestServiceState = new ServiceState();
+ mTestServiceState.setStateOutOfService();
+ mTestServiceStateForSubId1 = new ServiceState();
+ mTestServiceStateForSubId1.setStateOff();
+
+ // Mock out the actual phone state
+ ServiceStateProvider provider = new ServiceStateProvider() {
+ @Override
+ public ServiceState getServiceState(int subId) {
+ if (subId == 1) {
+ return mTestServiceStateForSubId1;
+ } else {
+ return mTestServiceState;
+ }
+ }
+
+ @Override
+ public int getDefaultSubId() {
+ return 0;
+ }
+ };
+ ProviderInfo providerInfo = new ProviderInfo();
+ providerInfo.authority = "service-state";
+ provider.attachInfoForTesting(mContext, providerInfo);
+ mContentResolver.addProvider("service-state", provider);
+ }
+
+ @Test
+ @SmallTest
+ public void testQueryServiceStateWithNoSubId() {
+ // Verify that when calling query with no subId in the uri the default ServiceState is
+ // returned.
+ // In this case the subId is set to 0 and the expected service state is
+ // mTestServiceState.
+ verifyServiceStateForSubId(ServiceStateTable.CONTENT_URI, mTestServiceState);
+ }
+
+ @Test
+ @SmallTest
+ public void testGetServiceStateWithDefaultSubId() {
+ // Verify that when calling with the DEFAULT_SUBSCRIPTION_ID the correct ServiceState is
+ // returned
+ // In this case the subId is set to 0 and the expected service state is
+ // mTestServiceState.
+ verifyServiceStateForSubId(
+ getUriForSubscriptionId(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID),
+ mTestServiceState);
+ }
+
+ /**
+ * Test querying the service state for a given subId
+ */
+ @Test
+ @SmallTest
+ public void testGetServiceStateForSubId() {
+ // Verify that when calling with a specific subId the correct ServiceState is returned
+ // In this case the subId is set to 1 and the expected service state is
+ // mTestServiceStateForSubId1
+ verifyServiceStateForSubId(getUriForSubscriptionId(1), mTestServiceStateForSubId1);
+ }
+
+ private void verifyServiceStateForSubId(Uri uri, ServiceState ss) {
+ Cursor cursor = mContentResolver.query(uri, mTestProjection, "",
+ null, null);
+ assertNotNull(cursor);
+ cursor.moveToFirst();
+
+ final int voiceRegState = ss.getVoiceRegState();
+ final int dataRegState = ss.getDataRegState();
+ final String voiceOperatorAlphaLong = ss.getOperatorAlphaLong();
+ final String voiceOperatorAlphaShort = ss.getOperatorAlphaShort();
+ final String voiceOperatorNumeric = ss.getOperatorNumeric();
+ final String dataOperatorAlphaLong = ss.getOperatorAlphaLong();
+ final String dataOperatorAlphaShort = ss.getOperatorAlphaShort();
+ final String dataOperatorNumeric = ss.getOperatorNumeric();
+ final int isManualNetworkSelection = (ss.getIsManualSelection()) ? 1 : 0;
+ final int rilVoiceRadioTechnology = ss.getRilVoiceRadioTechnology();
+ final int rilDataRadioTechnology = ss.getRilDataRadioTechnology();
+ final int cssIndicator = ss.getCssIndicator();
+ final int networkId = ss.getCdmaNetworkId();
+ final int systemId = ss.getCdmaSystemId();
+ final int cdmaRoamingIndicator = ss.getCdmaRoamingIndicator();
+ final int cdmaDefaultRoamingIndicator = ss.getCdmaDefaultRoamingIndicator();
+ final int cdmaEriIconIndex = ss.getCdmaEriIconIndex();
+ final int cdmaEriIconMode = ss.getCdmaEriIconMode();
+ final int isEmergencyOnly = (ss.isEmergencyOnly()) ? 1 : 0;
+ final int isUsingCarrierAggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
+ final String operatorAlphaLongRaw = ss.getOperatorAlphaLongRaw();
+ final String operatorAlphaShortRaw = ss.getOperatorAlphaShortRaw();
+
+ assertEquals(voiceRegState, cursor.getInt(0));
+ assertEquals(dataRegState, cursor.getInt(1));
+ assertEquals(voiceOperatorAlphaLong, cursor.getString(2));
+ assertEquals(voiceOperatorAlphaShort, cursor.getString(3));
+ assertEquals(voiceOperatorNumeric, cursor.getString(4));
+ assertEquals(dataOperatorAlphaLong, cursor.getString(5));
+ assertEquals(dataOperatorAlphaShort, cursor.getString(6));
+ assertEquals(dataOperatorNumeric, cursor.getString(7));
+ assertEquals(isManualNetworkSelection, cursor.getInt(8));
+ assertEquals(rilVoiceRadioTechnology, cursor.getInt(9));
+ assertEquals(rilDataRadioTechnology, cursor.getInt(10));
+ assertEquals(cssIndicator, cursor.getInt(11));
+ assertEquals(networkId, cursor.getInt(12));
+ assertEquals(systemId, cursor.getInt(13));
+ assertEquals(cdmaRoamingIndicator, cursor.getInt(14));
+ assertEquals(cdmaDefaultRoamingIndicator, cursor.getInt(15));
+ assertEquals(cdmaEriIconIndex, cursor.getInt(16));
+ assertEquals(cdmaEriIconMode, cursor.getInt(17));
+ assertEquals(isEmergencyOnly, cursor.getInt(18));
+ assertEquals(isUsingCarrierAggregation, cursor.getInt(19));
+ assertEquals(operatorAlphaLongRaw, cursor.getString(20));
+ assertEquals(operatorAlphaShortRaw, cursor.getString(21));
+ }
+
+ /**
+ * Test that we don't notify for certain field changes. (e.g. we don't notify when the NetworkId
+ * or SystemId change) This is an intentional behavior change from the broadcast.
+ */
+ @Test
+ @SmallTest
+ public void testNoNotify() {
+ int subId = 0;
+
+ ServiceState oldSS = new ServiceState();
+ oldSS.setStateOutOfService();
+ oldSS.setCdmaSystemAndNetworkId(1, 1);
+
+ ServiceState newSS = new ServiceState();
+ newSS.setStateOutOfService();
+ newSS.setCdmaSystemAndNetworkId(0, 0);
+
+ // Test that notifyChange is not called for these fields
+ boolean notifyChangeWasCalled = false;
+ try {
+ ServiceStateProvider.notifyChangeForSubIdAndField(mContext, oldSS, newSS, subId);
+ } catch (RuntimeException e) {
+ final String message = e.getMessage();
+ if (message != null && message.equals("notifyChange!")) {
+ notifyChangeWasCalled = true;
+ }
+ }
+ assertFalse(notifyChangeWasCalled);
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifyChanged() {
+ int subId = 0;
+
+ ServiceState oldSS = new ServiceState();
+ oldSS.setStateOutOfService();
+ oldSS.setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE);
+
+ ServiceState copyOfOldSS = new ServiceState();
+ copyOfOldSS.setStateOutOfService();
+ copyOfOldSS.setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE);
+
+ ServiceState newSS = new ServiceState();
+ newSS.setStateOutOfService();
+ newSS.setVoiceRegState(ServiceState.STATE_POWER_OFF);
+
+ // Test that notifyChange is not called with no change in notifyChangeForSubIdAndField
+ boolean notifyChangeWasCalled = false;
+ try {
+ ServiceStateProvider.notifyChangeForSubIdAndField(mContext, oldSS, copyOfOldSS, subId);
+ } catch (RuntimeException e) {
+ final String message = e.getMessage();
+ if (message != null && message.equals("notifyChange!")) {
+ notifyChangeWasCalled = true;
+ }
+ }
+ assertFalse(notifyChangeWasCalled);
+
+ // Test that notifyChange is not called with no change in notifyChangeForSubId
+ notifyChangeWasCalled = false;
+ try {
+ ServiceStateProvider.notifyChangeForSubId(mContext, oldSS, copyOfOldSS, subId);
+ } catch (RuntimeException e) {
+ final String message = e.getMessage();
+ if (message != null && message.equals("notifyChange!")) {
+ notifyChangeWasCalled = true;
+ }
+ }
+ assertFalse(notifyChangeWasCalled);
+
+ // Test that notifyChange is called by notifyChangeForSubIdAndField when the voice_reg_state
+ // changes
+ notifyChangeWasCalled = false;
+ try {
+ ServiceStateProvider.notifyChangeForSubIdAndField(mContext, oldSS, newSS, subId);
+ } catch (RuntimeException e) {
+ final String message = e.getMessage();
+ if (message != null && message.equals("notifyChange!")) {
+ notifyChangeWasCalled = true;
+ }
+ }
+ assertTrue(notifyChangeWasCalled);
+
+ // Test that notifyChange is called by notifyChangeForSubId when the voice_reg_state changes
+ notifyChangeWasCalled = false;
+ try {
+ ServiceStateProvider.notifyChangeForSubId(mContext, oldSS, newSS, subId);
+ } catch (RuntimeException e) {
+ final String message = e.getMessage();
+ if (message != null && message.equals("notifyChange!")) {
+ notifyChangeWasCalled = true;
+ }
+ }
+ assertTrue(notifyChangeWasCalled);
+ }
+}